Prevent unsound promoted bounds from being used as arguments
Fixes https://github.com/dart-lang/sdk/issues/35100
Change-Id: I65f73f40d0ea970f0631599e01b177412fc32a40
Reviewed-on: https://dart-review.googlesource.com/c/90390
Commit-Queue: Peter von der Ahé <ahe@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@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 d4a2582..2688486 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -5446,6 +5446,46 @@
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<
Message Function(
+ String name,
+ DartType _type,
+ DartType
+ _type2)> templateIntersectionTypeAsTypeArgument = const Template<
+ Message Function(String name, DartType _type, DartType _type2)>(
+ messageTemplate:
+ r"""Can't infer a type for '#name', it can be either '#type' or '#type2'.""",
+ tipTemplate:
+ r"""Try adding a type argument selecting one of the options.""",
+ withArguments: _withArgumentsIntersectionTypeAsTypeArgument);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name, DartType _type, DartType _type2)>
+ codeIntersectionTypeAsTypeArgument =
+ const Code<Message Function(String name, DartType _type, DartType _type2)>(
+ "IntersectionTypeAsTypeArgument",
+ templateIntersectionTypeAsTypeArgument,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIntersectionTypeAsTypeArgument(
+ String name, DartType _type, DartType _type2) {
+ if (name.isEmpty) throw 'No name provided';
+ name = demangleMixinApplicationName(name);
+ TypeLabeler labeler = new TypeLabeler();
+ List<Object> typeParts = labeler.labelType(_type);
+ List<Object> type2Parts = labeler.labelType(_type2);
+ String type = typeParts.join();
+ String type2 = type2Parts.join();
+ return new Message(codeIntersectionTypeAsTypeArgument,
+ message:
+ """Can't infer a type for '${name}', it can be either '${type}' or '${type2}'.""" +
+ labeler.originMessages,
+ tip: """Try adding a type argument selecting one of the options.""",
+ arguments: {'name': name, 'type': _type, 'type2': _type2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+ Message Function(
DartType _type,
DartType
_type2)> templateInvalidAssignment = const Template<
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 dff43f5..4a946c1 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
@@ -83,6 +83,7 @@
templateIncorrectTypeArgumentInferred,
templateIncorrectTypeArgumentQualified,
templateIncorrectTypeArgumentQualifiedInferred,
+ templateIntersectionTypeAsTypeArgument,
templateLoadLibraryHidesMember,
templateLocalDefinitionHidesExport,
templateLocalDefinitionHidesImport,
@@ -1459,6 +1460,15 @@
message = messageGenericFunctionTypeUsedAsActualTypeArgument;
}
typeParameter = null;
+ } else if (argument is TypeParameterType &&
+ argument.promotedBound != null) {
+ addProblem(
+ templateIntersectionTypeAsTypeArgument.withArguments(
+ typeParameter.name, argument, argument.promotedBound),
+ offset,
+ noLength,
+ fileUri);
+ continue;
} else {
if (issue.enclosingType == null && targetReceiver != null) {
if (issueInferred) {
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index b88b3a1..762dc4f 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -212,6 +212,7 @@
InputFileNotFound/example: Fail
IntegerLiteralIsOutOfRange/example: Fail
InterpolationInUri/example: Fail
+IntersectionTypeAsTypeArgument/analyzerCode: Fail # Analyzer doesn't catch this error.
InvalidBreakTarget/analyzerCode: Fail
InvalidBreakTarget/example: Fail
InvalidCastFunctionExpr/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 79f6350..847b9cb 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -3321,6 +3321,15 @@
template: "This is the type variable whose bound isn't conformed to."
severity: CONTEXT
+IntersectionTypeAsTypeArgument:
+ template: "Can't infer a type for '#name', it can be either '#type' or '#type2'."
+ tip: "Try adding a type argument selecting one of the options."
+ script: |
+ class A {}
+ class B extends A {}
+ f<T>(T t) => null;
+ g<S>(S t) => t is B ? f(t) : null;
+
InferredPackageUri:
template: "Interpreting this as package URI, '#uri'."
severity: WARNING
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index 165a679..1fde8dc 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -150,5 +150,6 @@
spread_collection: RuntimeError # Should be fixed as part of implementing spread collection support
type_variable_as_super: RuntimeError
type_variable_prefix: RuntimeError
+unsound_promotion: RuntimeError
void_methods: ExpectationFileMismatch
warn_unresolved_sends: InstrumentationMismatch # Test assumes Dart 1.0 semantics
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index 8ac7058..a8ef9c4 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -977,6 +977,7 @@
undefined_getter_in_compound_assignment: TextSerializationFailure # Was: Pass
undefined: TextSerializationFailure # Was: Pass
uninitialized_fields: TextSerializationFailure # Was: Pass
+unsound_promotion: TextSerializationFailure
unused_methods: TextSerializationFailure # Was: Pass
var_as_type_name: TextSerializationFailure # Was: Pass
void_methods: ExpectationFileMismatch
diff --git a/pkg/front_end/testcases/unsound_promotion.dart b/pkg/front_end/testcases/unsound_promotion.dart
new file mode 100644
index 0000000..f04da5f
--- /dev/null
+++ b/pkg/front_end/testcases/unsound_promotion.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2019, 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.
+
+class A {}
+
+class B {}
+
+class C extends B implements A {}
+
+List<A> list;
+
+List<T> g<T extends A>(T t) {
+ list = <T>[];
+ print(list.runtimeType);
+ return list;
+}
+
+List<S> f<S>(S s) {
+ if (s is A) {
+ var list = g(s);
+ return list;
+ }
+ return null;
+}
+
+main() {
+ f<B>(new C());
+ print(list.runtimeType);
+ List<A> aList;
+ aList = list;
+ Object o = aList;
+ aList = o;
+}
diff --git a/pkg/front_end/testcases/unsound_promotion.dart.hierarchy.expect b/pkg/front_end/testcases/unsound_promotion.dart.hierarchy.expect
new file mode 100644
index 0000000..c1e95c6
--- /dev/null
+++ b/pkg/front_end/testcases/unsound_promotion.dart.hierarchy.expect
@@ -0,0 +1,83 @@
+Object:
+ superclasses:
+ interfaces:
+ classMembers:
+ Object._haveSameRuntimeType
+ Object.toString
+ Object.runtimeType
+ Object._toString
+ Object._simpleInstanceOf
+ Object._hashCodeRnd
+ Object._instanceOf
+ Object.noSuchMethod
+ Object._objectHashCode
+ Object._identityHashCode
+ Object.hashCode
+ Object._simpleInstanceOfFalse
+ Object._simpleInstanceOfTrue
+ Object.==
+ classSetters:
+
+A:
+ superclasses:
+ Object
+ interfaces:
+ classMembers:
+ Object.toString
+ Object.runtimeType
+ Object._simpleInstanceOf
+ Object._instanceOf
+ Object.noSuchMethod
+ Object._identityHashCode
+ Object.hashCode
+ Object._simpleInstanceOfFalse
+ Object._simpleInstanceOfTrue
+ Object.==
+ classSetters:
+
+B:
+ superclasses:
+ Object
+ interfaces:
+ classMembers:
+ Object.toString
+ Object.runtimeType
+ Object._simpleInstanceOf
+ Object._instanceOf
+ Object.noSuchMethod
+ Object._identityHashCode
+ Object.hashCode
+ Object._simpleInstanceOfFalse
+ Object._simpleInstanceOfTrue
+ Object.==
+ classSetters:
+
+C:
+ superclasses:
+ Object
+ -> B
+ interfaces: A
+ classMembers:
+ Object.toString
+ Object.runtimeType
+ Object._simpleInstanceOf
+ Object._instanceOf
+ Object.noSuchMethod
+ Object._identityHashCode
+ Object.hashCode
+ Object._simpleInstanceOfFalse
+ Object._simpleInstanceOfTrue
+ Object.==
+ classSetters:
+ interfaceMembers:
+ Object.toString
+ Object.runtimeType
+ Object._simpleInstanceOf
+ Object._instanceOf
+ Object.noSuchMethod
+ Object._identityHashCode
+ Object.hashCode
+ Object._simpleInstanceOfFalse
+ Object._simpleInstanceOfTrue
+ Object.==
+ interfaceSetters:
diff --git a/pkg/front_end/testcases/unsound_promotion.dart.legacy.expect b/pkg/front_end/testcases/unsound_promotion.dart.legacy.expect
new file mode 100644
index 0000000..fc1dfe0
--- /dev/null
+++ b/pkg/front_end/testcases/unsound_promotion.dart.legacy.expect
@@ -0,0 +1,40 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+}
+class B extends core::Object {
+ synthetic constructor •() → self::B
+ : super core::Object::•()
+ ;
+}
+class C extends self::B implements self::A {
+ synthetic constructor •() → self::C
+ : super self::B::•()
+ ;
+}
+static field core::List<self::A> list;
+static method g<T extends self::A = dynamic>(self::g::T t) → core::List<self::g::T> {
+ self::list = <self::g::T>[];
+ core::print(self::list.runtimeType);
+ return self::list;
+}
+static method f<S extends core::Object = dynamic>(self::f::S s) → core::List<self::f::S> {
+ if(s is self::A) {
+ dynamic list = self::g<dynamic>(s);
+ return list;
+ }
+ return null;
+}
+static method main() → dynamic {
+ self::f<self::B>(new self::C::•());
+ core::print(self::list.runtimeType);
+ core::List<self::A> aList;
+ aList = self::list;
+ core::Object o = aList;
+ aList = o;
+}
diff --git a/pkg/front_end/testcases/unsound_promotion.dart.legacy.transformed.expect b/pkg/front_end/testcases/unsound_promotion.dart.legacy.transformed.expect
new file mode 100644
index 0000000..fc1dfe0
--- /dev/null
+++ b/pkg/front_end/testcases/unsound_promotion.dart.legacy.transformed.expect
@@ -0,0 +1,40 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+}
+class B extends core::Object {
+ synthetic constructor •() → self::B
+ : super core::Object::•()
+ ;
+}
+class C extends self::B implements self::A {
+ synthetic constructor •() → self::C
+ : super self::B::•()
+ ;
+}
+static field core::List<self::A> list;
+static method g<T extends self::A = dynamic>(self::g::T t) → core::List<self::g::T> {
+ self::list = <self::g::T>[];
+ core::print(self::list.runtimeType);
+ return self::list;
+}
+static method f<S extends core::Object = dynamic>(self::f::S s) → core::List<self::f::S> {
+ if(s is self::A) {
+ dynamic list = self::g<dynamic>(s);
+ return list;
+ }
+ return null;
+}
+static method main() → dynamic {
+ self::f<self::B>(new self::C::•());
+ core::print(self::list.runtimeType);
+ core::List<self::A> aList;
+ aList = self::list;
+ core::Object o = aList;
+ aList = o;
+}
diff --git a/pkg/front_end/testcases/unsound_promotion.dart.outline.expect b/pkg/front_end/testcases/unsound_promotion.dart.outline.expect
new file mode 100644
index 0000000..9503f5c
--- /dev/null
+++ b/pkg/front_end/testcases/unsound_promotion.dart.outline.expect
@@ -0,0 +1,23 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ ;
+}
+class B extends core::Object {
+ synthetic constructor •() → self::B
+ ;
+}
+class C extends self::B implements self::A {
+ synthetic constructor •() → self::C
+ ;
+}
+static field core::List<self::A> list;
+static method g<T extends self::A = dynamic>(self::g::T t) → core::List<self::g::T>
+ ;
+static method f<S extends core::Object = dynamic>(self::f::S s) → core::List<self::f::S>
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/unsound_promotion.dart.strong.expect b/pkg/front_end/testcases/unsound_promotion.dart.strong.expect
new file mode 100644
index 0000000..4f9cda5
--- /dev/null
+++ b/pkg/front_end/testcases/unsound_promotion.dart.strong.expect
@@ -0,0 +1,49 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/unsound_promotion.dart:21:16: Error: Can't infer a type for 'T', it can be either 'S' or 'A'.
+// - 'A' is from 'pkg/front_end/testcases/unsound_promotion.dart'.
+// Try adding a type argument selecting one of the options.
+// var list = g(s);
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+}
+class B extends core::Object {
+ synthetic constructor •() → self::B
+ : super core::Object::•()
+ ;
+}
+class C extends self::B implements self::A {
+ synthetic constructor •() → self::C
+ : super self::B::•()
+ ;
+}
+static field core::List<self::A> list;
+static method g<T extends self::A = self::A>(self::g::T t) → core::List<self::g::T> {
+ self::list = <self::g::T>[];
+ core::print(self::list.{core::Object::runtimeType});
+ return self::list as{TypeError} core::List<self::g::T>;
+}
+static method f<S extends core::Object = dynamic>(self::f::S s) → core::List<self::f::S> {
+ if(s is self::A) {
+ core::List<self::f::S extends self::A> list = self::g<self::f::S extends self::A>(s{self::f::S extends self::A});
+ return list;
+ }
+ return null;
+}
+static method main() → dynamic {
+ self::f<self::B>(new self::C::•());
+ core::print(self::list.{core::Object::runtimeType});
+ core::List<self::A> aList;
+ aList = self::list;
+ core::Object o = aList;
+ aList = o as{TypeError} core::List<self::A>;
+}
diff --git a/pkg/front_end/testcases/unsound_promotion.dart.strong.transformed.expect b/pkg/front_end/testcases/unsound_promotion.dart.strong.transformed.expect
new file mode 100644
index 0000000..4f9cda5
--- /dev/null
+++ b/pkg/front_end/testcases/unsound_promotion.dart.strong.transformed.expect
@@ -0,0 +1,49 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/unsound_promotion.dart:21:16: Error: Can't infer a type for 'T', it can be either 'S' or 'A'.
+// - 'A' is from 'pkg/front_end/testcases/unsound_promotion.dart'.
+// Try adding a type argument selecting one of the options.
+// var list = g(s);
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+}
+class B extends core::Object {
+ synthetic constructor •() → self::B
+ : super core::Object::•()
+ ;
+}
+class C extends self::B implements self::A {
+ synthetic constructor •() → self::C
+ : super self::B::•()
+ ;
+}
+static field core::List<self::A> list;
+static method g<T extends self::A = self::A>(self::g::T t) → core::List<self::g::T> {
+ self::list = <self::g::T>[];
+ core::print(self::list.{core::Object::runtimeType});
+ return self::list as{TypeError} core::List<self::g::T>;
+}
+static method f<S extends core::Object = dynamic>(self::f::S s) → core::List<self::f::S> {
+ if(s is self::A) {
+ core::List<self::f::S extends self::A> list = self::g<self::f::S extends self::A>(s{self::f::S extends self::A});
+ return list;
+ }
+ return null;
+}
+static method main() → dynamic {
+ self::f<self::B>(new self::C::•());
+ core::print(self::list.{core::Object::runtimeType});
+ core::List<self::A> aList;
+ aList = self::list;
+ core::Object o = aList;
+ aList = o as{TypeError} core::List<self::A>;
+}
diff --git a/pkg/front_end/testcases/unsound_promotion.dart.type_promotion.expect b/pkg/front_end/testcases/unsound_promotion.dart.type_promotion.expect
new file mode 100644
index 0000000..c175828
--- /dev/null
+++ b/pkg/front_end/testcases/unsound_promotion.dart.type_promotion.expect
@@ -0,0 +1,9 @@
+pkg/front_end/testcases/unsound_promotion.dart:20:9: Context: Possible promotion of s@397
+ if (s is A) {
+ ^^
+pkg/front_end/testcases/unsound_promotion.dart:31:9: Context: Write to aList@541
+ aList = list;
+ ^
+pkg/front_end/testcases/unsound_promotion.dart:33:9: Context: Write to aList@541
+ aList = o;
+ ^
diff --git a/pkg/kernel/lib/src/bounds_checks.dart b/pkg/kernel/lib/src/bounds_checks.dart
index 5dc341f..907865a 100644
--- a/pkg/kernel/lib/src/bounds_checks.dart
+++ b/pkg/kernel/lib/src/bounds_checks.dart
@@ -351,7 +351,10 @@
}
for (int i = 0; i < arguments.length; ++i) {
DartType argument = arguments[i];
- if (argument is FunctionType && argument.typeParameters.length > 0) {
+ if (argument is TypeParameterType && argument.promotedBound != null) {
+ result ??= <TypeArgumentIssue>[];
+ result.add(new TypeArgumentIssue(argument, parameters[i], null));
+ } else if (argument is FunctionType && argument.typeParameters.length > 0) {
// Generic function types aren't allowed as type arguments either.
result ??= <TypeArgumentIssue>[];
result.add(new TypeArgumentIssue(argument, parameters[i], null));