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));