[fasta] Reject generic function types in bounds of type variables
Fixes #33423.
Bug: http://dartbug.com/33423
Change-Id: Iff2a23ef456ebc05fcce8ea804b478db313cac1e
Reviewed-on: https://dart-review.googlesource.com/64544
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index a42ad93..cae26c2 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -2931,6 +2931,19 @@
message: r"""'sync*' and 'async*' can't return a value.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeGenericFunctionTypeInBound =
+ messageGenericFunctionTypeInBound;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageGenericFunctionTypeInBound = const MessageCode(
+ "GenericFunctionTypeInBound",
+ analyzerCode: "GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND",
+ dart2jsCode: "*fatal*",
+ severity: Severity.error,
+ message:
+ r"""Type variables can't have generic function types in their bounds.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(String name)> templateGetterNotFound =
const Template<Message Function(String name)>(
messageTemplate: r"""Getter not found: '#name'.""",
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
index 7821f30..efbcf07 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
@@ -39,6 +39,7 @@
LocatedMessage,
Message,
messageConflictsWithTypeVariableCause,
+ messageGenericFunctionTypeInBound,
messageTypeVariableDuplicatedName,
messageTypeVariableSameNameAsEnclosing,
noLength,
@@ -115,6 +116,7 @@
import 'type_algorithms.dart'
show
calculateBounds,
+ findGenericFunctionTypes,
getNonSimplicityIssuesForDeclaration,
getNonSimplicityIssuesForTypeVariables;
@@ -1107,13 +1109,30 @@
bool strongMode) {
if (variables == null) return 0;
+ bool haveErroneousBounds = false;
if (strongMode) {
- List<KernelTypeBuilder> calculatedBounds =
- calculateBounds(variables, dynamicType, bottomType, objectClass);
for (int i = 0; i < variables.length; ++i) {
- variables[i].defaultType = calculatedBounds[i];
+ TypeVariableBuilder<TypeBuilder, Object> variable = variables[i];
+ List<TypeBuilder> genericFunctionTypes = <TypeBuilder>[];
+ findGenericFunctionTypes(variable.bound,
+ result: genericFunctionTypes);
+ if (genericFunctionTypes.length > 0) {
+ haveErroneousBounds = true;
+ addProblem(messageGenericFunctionTypeInBound, variable.charOffset,
+ variable.name.length, variable.fileUri);
+ }
}
- } else {
+
+ if (!haveErroneousBounds) {
+ List<KernelTypeBuilder> calculatedBounds =
+ calculateBounds(variables, dynamicType, bottomType, objectClass);
+ for (int i = 0; i < variables.length; ++i) {
+ variables[i].defaultType = calculatedBounds[i];
+ }
+ }
+ }
+
+ if (!strongMode || haveErroneousBounds) {
// In Dart 1, put `dynamic` everywhere.
for (int i = 0; i < variables.length; ++i) {
variables[i].defaultType = dynamicType;
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
index 81b72d7..a45b739 100644
--- a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
@@ -693,3 +693,29 @@
issues.addAll(convertRawTypeCyclesIntoIssues(declaration, cyclesToReport));
return issues;
}
+
+void findGenericFunctionTypes(TypeBuilder type, {List<TypeBuilder> result}) {
+ result ??= <TypeBuilder>[];
+ if (type is FunctionTypeBuilder) {
+ if (type.typeVariables != null && type.typeVariables.length > 0) {
+ result.add(type);
+
+ for (TypeVariableBuilder<TypeBuilder, Object> typeVariable
+ in type.typeVariables) {
+ findGenericFunctionTypes(typeVariable.bound, result: result);
+ findGenericFunctionTypes(typeVariable.defaultType, result: result);
+ }
+ }
+ findGenericFunctionTypes(type.returnType, result: result);
+ if (type.formals != null) {
+ for (FormalParameterBuilder<TypeBuilder> formal in type.formals) {
+ findGenericFunctionTypes(formal.type, result: result);
+ }
+ }
+ } else if (type is NamedTypeBuilder<TypeBuilder, Object> &&
+ type.arguments != null) {
+ for (TypeBuilder argument in type.arguments) {
+ findGenericFunctionTypes(argument, result: result);
+ }
+ }
+}
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index cf7d567..d90f6c5 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -2518,3 +2518,11 @@
factory Foo.bar() = Foo.foo;
}
main() { var foo = new Foo.foo(); }
+
+GenericFunctionTypeInBound:
+ template: "Type variables can't have generic function types in their bounds."
+ analyzerCode: GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND
+ dart2jsCode: "*fatal*"
+ severity: ERROR
+ script: |
+ class Hest<X extends Y Function<Y>(Y)> {}
diff --git a/pkg/front_end/testcases/instantiate_to_bound/non_simple_generic_function_in_bound_regress.dart.strong.expect b/pkg/front_end/testcases/instantiate_to_bound/non_simple_generic_function_in_bound_regress.dart.strong.expect
index 372b447..c807c38 100644
--- a/pkg/front_end/testcases/instantiate_to_bound/non_simple_generic_function_in_bound_regress.dart.strong.expect
+++ b/pkg/front_end/testcases/instantiate_to_bound/non_simple_generic_function_in_bound_regress.dart.strong.expect
@@ -1,3 +1,9 @@
+// Errors:
+//
+// pkg/front_end/testcases/instantiate_to_bound/non_simple_generic_function_in_bound_regress.dart:11:12: Error: Type variables can't have generic function types in their bounds.
+// class Fisk<TypeY extends Function<TypeZ extends Hest<Null>>(TypeZ)> {}
+// ^^^^^
+
library;
import self as self;
import "dart:core" as core;
@@ -7,7 +13,7 @@
: super core::Object::•()
;
}
-class Fisk<TypeY extends <TypeZ extends self::Hest<core::Null> = dynamic>(TypeZ) → dynamic = <TypeZ extends self::Hest<core::Null> = dynamic>(TypeZ) → dynamic> extends core::Object {
+class Fisk<TypeY extends <TypeZ extends self::Hest<core::Null> = dynamic>(TypeZ) → dynamic = dynamic> extends core::Object {
synthetic constructor •() → void
: super core::Object::•()
;
diff --git a/pkg/front_end/testcases/instantiate_to_bound/non_simple_generic_function_in_bound_regress.dart.strong.transformed.expect b/pkg/front_end/testcases/instantiate_to_bound/non_simple_generic_function_in_bound_regress.dart.strong.transformed.expect
index 372b447..c807c38 100644
--- a/pkg/front_end/testcases/instantiate_to_bound/non_simple_generic_function_in_bound_regress.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/instantiate_to_bound/non_simple_generic_function_in_bound_regress.dart.strong.transformed.expect
@@ -1,3 +1,9 @@
+// Errors:
+//
+// pkg/front_end/testcases/instantiate_to_bound/non_simple_generic_function_in_bound_regress.dart:11:12: Error: Type variables can't have generic function types in their bounds.
+// class Fisk<TypeY extends Function<TypeZ extends Hest<Null>>(TypeZ)> {}
+// ^^^^^
+
library;
import self as self;
import "dart:core" as core;
@@ -7,7 +13,7 @@
: super core::Object::•()
;
}
-class Fisk<TypeY extends <TypeZ extends self::Hest<core::Null> = dynamic>(TypeZ) → dynamic = <TypeZ extends self::Hest<core::Null> = dynamic>(TypeZ) → dynamic> extends core::Object {
+class Fisk<TypeY extends <TypeZ extends self::Hest<core::Null> = dynamic>(TypeZ) → dynamic = dynamic> extends core::Object {
synthetic constructor •() → void
: super core::Object::•()
;
diff --git a/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart
new file mode 100644
index 0000000..cdf7460
--- /dev/null
+++ b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This test checks that generic function types are rejected with an appropriate
+// compile-time error message if encountered in bounds of type variables.
+
+class Hest<TypeX extends TypeY Function<TypeY>(TypeY)> {}
+
+main() {}
diff --git a/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.direct.expect b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.direct.expect
new file mode 100644
index 0000000..8abcc85
--- /dev/null
+++ b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.direct.expect
@@ -0,0 +1,10 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Hest<TypeX extends <TypeY extends core::Object = dynamic>(TypeY) → TypeY = dynamic> extends core::Object {
+ synthetic constructor •() → void
+ : super core::Object::•()
+ ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.direct.transformed.expect b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.direct.transformed.expect
new file mode 100644
index 0000000..8abcc85
--- /dev/null
+++ b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.direct.transformed.expect
@@ -0,0 +1,10 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Hest<TypeX extends <TypeY extends core::Object = dynamic>(TypeY) → TypeY = dynamic> extends core::Object {
+ synthetic constructor •() → void
+ : super core::Object::•()
+ ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.outline.expect b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.outline.expect
new file mode 100644
index 0000000..1149d7f
--- /dev/null
+++ b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.outline.expect
@@ -0,0 +1,10 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Hest<TypeX extends <TypeY extends core::Object = dynamic>(TypeY) → TypeY = dynamic> extends core::Object {
+ synthetic constructor •() → void
+ ;
+}
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.strong.expect b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.strong.expect
new file mode 100644
index 0000000..8bd0c4b
--- /dev/null
+++ b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.strong.expect
@@ -0,0 +1,16 @@
+// Errors:
+//
+// pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart:8:12: Error: Type variables can't have generic function types in their bounds.
+// class Hest<TypeX extends TypeY Function<TypeY>(TypeY)> {}
+// ^^^^^
+
+library;
+import self as self;
+import "dart:core" as core;
+
+class Hest<TypeX extends <TypeY extends core::Object = dynamic>(TypeY) → TypeY = dynamic> extends core::Object {
+ synthetic constructor •() → void
+ : super core::Object::•()
+ ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.strong.transformed.expect b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.strong.transformed.expect
new file mode 100644
index 0000000..8bd0c4b
--- /dev/null
+++ b/pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart.strong.transformed.expect
@@ -0,0 +1,16 @@
+// Errors:
+//
+// pkg/front_end/testcases/reject_generic_function_types_in_bounds.dart:8:12: Error: Type variables can't have generic function types in their bounds.
+// class Hest<TypeX extends TypeY Function<TypeY>(TypeY)> {}
+// ^^^^^
+
+library;
+import self as self;
+import "dart:core" as core;
+
+class Hest<TypeX extends <TypeY extends core::Object = dynamic>(TypeY) → TypeY = dynamic> extends core::Object {
+ synthetic constructor •() → void
+ : super core::Object::•()
+ ;
+}
+static method main() → dynamic {}