[cfe] Add explicit type argument to .map(...).toList()

Expressions like

  List<VariableDeclaration> variables = ...
  List<Expression> reads = variables.map((v) => VariableGet(v)).toList()

don't get the intended type argument `<Expression>` through inference,
but instead the type inferred from the return type of the closure, in
this case `<VariableGet>`.

This causes problems when used with the AST which assumes that
expressions are interchangeable wherever they are used, and is likely
the cause of https://github.com/flutter/flutter/issues/102077 .

This CL adds an explicit type argument in these cases.

TEST=general/infer_map

Change-Id: I0bcae21f06c54f290dc20686b0d84c065d8ea04c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241605
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
index 4a20a40..57b3a31 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
@@ -179,7 +179,7 @@
           StringLiteral(_getExtensionMemberName(node)),
           ListLiteral(function.positionalParameters
               .sublist(1)
-              .map((argument) => VariableGet(argument))
+              .map<Expression>((argument) => VariableGet(argument))
               .toList())
         ], types: [
           function.returnType
@@ -294,8 +294,8 @@
     }
 
     // Lower arguments in other kinds of Lists.
-    var callUncheckedArguments;
-    var entryType;
+    List<Expression> callUncheckedArguments;
+    DartType entryType;
     if (argumentsList is ListLiteral) {
       if (argumentsList.expressions.length >= callUncheckedTargets.length) {
         return node;
@@ -309,7 +309,7 @@
         return node;
       }
       callUncheckedArguments = argumentsListConstant.entries
-          .map((constant) => ConstantExpression(
+          .map<Expression>((constant) => ConstantExpression(
               constant, constant.getType(_staticTypeContext)))
           .toList();
       entryType = argumentsListConstant.typeArgument;
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 76086f5..9d05fc1 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -191,8 +191,9 @@
             .getTopLevelProcedure('dart:core', '_createInvocationMirror'),
         ir.Arguments(<ir.Expression>[
           ir.StringLiteral(name)..fileOffset = offset,
-          ir.ListLiteral(
-              arguments.types.map((t) => ir.TypeLiteral(t)).toList()),
+          ir.ListLiteral(arguments.types
+              .map<ir.Expression>((t) => ir.TypeLiteral(t))
+              .toList()),
           ir.ListLiteral(arguments.positional)..fileOffset = offset,
           ir.MapLiteral(List<ir.MapLiteralEntry>.from(
               arguments.named.map((ir.NamedExpression arg) {
diff --git a/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart b/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart
index c363409..17d5d77 100644
--- a/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart
@@ -92,7 +92,7 @@
   static Expression _makeForwardingCall(
       Member target, List<DartType> typeArguments, FunctionNode function) {
     final List<Expression> positional = function.positionalParameters
-        .map((v) => new VariableGet(v)..fileOffset = v.fileOffset)
+        .map<Expression>((v) => new VariableGet(v)..fileOffset = v.fileOffset)
         .toList();
     final List<NamedExpression> named = function.namedParameters
         .map((v) => new NamedExpression(
diff --git a/pkg/front_end/testcases/general/infer_map.dart b/pkg/front_end/testcases/general/infer_map.dart
new file mode 100644
index 0000000..af8b81d
--- /dev/null
+++ b/pkg/front_end/testcases/general/infer_map.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2022, 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 extends A {}
+
+List<int> intList = [1, 2];
+
+List<A> list1 = intList.map((i) => new B()).toList();
+
+test(List<A> list) {
+  try {
+    list.add(new A());
+    list.removeLast();
+  } catch (e) {
+    return;
+  }
+  throw 'Expected subtype error';
+}
+
+main() {
+  test(list1);
+  list1 = intList.map((i) => new B()).toList();
+  test(list1);
+
+  List<A> list2 = intList.map((i) => new B()).toList();
+  test(list2);
+  list2 = intList.map((i) => new B()).toList();
+  test(list2);
+
+  test(intList.map((i) => new B()).toList());
+}
diff --git a/pkg/front_end/testcases/general/infer_map.dart.textual_outline.expect b/pkg/front_end/testcases/general/infer_map.dart.textual_outline.expect
new file mode 100644
index 0000000..d62af11
--- /dev/null
+++ b/pkg/front_end/testcases/general/infer_map.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+class A {}
+
+class B extends A {}
+
+List<int> intList = [1, 2];
+List<A> list1 = intList.map((i) => new B()).toList();
+test(List<A> list) {}
+main() {}
diff --git a/pkg/front_end/testcases/general/infer_map.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/infer_map.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..3683c37
--- /dev/null
+++ b/pkg/front_end/testcases/general/infer_map.dart.textual_outline_modelled.expect
@@ -0,0 +1,9 @@
+List<A> list1 = intList.map((i) => new B()).toList();
+List<int> intList = [1, 2];
+
+class A {}
+
+class B extends A {}
+
+main() {}
+test(List<A> list) {}
diff --git a/pkg/front_end/testcases/general/infer_map.dart.weak.expect b/pkg/front_end/testcases/general/infer_map.dart.weak.expect
new file mode 100644
index 0000000..d1501d8
--- /dev/null
+++ b/pkg/front_end/testcases/general/infer_map.dart.weak.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class B extends self::A {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+}
+static field core::List<core::int> intList = <core::int>[1, 2];
+static field core::List<self::A> list1 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+static method test(core::List<self::A> list) → dynamic {
+  try {
+    list.{core::List::add}(new self::A::•()){(self::A) → void};
+    list.{core::List::removeLast}(){() → self::A};
+  }
+  on core::Object catch(final core::Object e) {
+    return;
+  }
+  throw "Expected subtype error";
+}
+static method main() → dynamic {
+  self::test(self::list1);
+  self::list1 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+  self::test(self::list1);
+  core::List<self::A> list2 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+  self::test(list2);
+  list2 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+  self::test(list2);
+  self::test(self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>});
+}
diff --git a/pkg/front_end/testcases/general/infer_map.dart.weak.modular.expect b/pkg/front_end/testcases/general/infer_map.dart.weak.modular.expect
new file mode 100644
index 0000000..d1501d8
--- /dev/null
+++ b/pkg/front_end/testcases/general/infer_map.dart.weak.modular.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class B extends self::A {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+}
+static field core::List<core::int> intList = <core::int>[1, 2];
+static field core::List<self::A> list1 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+static method test(core::List<self::A> list) → dynamic {
+  try {
+    list.{core::List::add}(new self::A::•()){(self::A) → void};
+    list.{core::List::removeLast}(){() → self::A};
+  }
+  on core::Object catch(final core::Object e) {
+    return;
+  }
+  throw "Expected subtype error";
+}
+static method main() → dynamic {
+  self::test(self::list1);
+  self::list1 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+  self::test(self::list1);
+  core::List<self::A> list2 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+  self::test(list2);
+  list2 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+  self::test(list2);
+  self::test(self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>});
+}
diff --git a/pkg/front_end/testcases/general/infer_map.dart.weak.outline.expect b/pkg/front_end/testcases/general/infer_map.dart.weak.outline.expect
new file mode 100644
index 0000000..f7b42fa
--- /dev/null
+++ b/pkg/front_end/testcases/general/infer_map.dart.weak.outline.expect
@@ -0,0 +1,18 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    ;
+}
+class B extends self::A {
+  synthetic constructor •() → self::B
+    ;
+}
+static field core::List<core::int> intList;
+static field core::List<self::A> list1;
+static method test(core::List<self::A> list) → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/general/infer_map.dart.weak.transformed.expect b/pkg/front_end/testcases/general/infer_map.dart.weak.transformed.expect
new file mode 100644
index 0000000..8d6c8ba
--- /dev/null
+++ b/pkg/front_end/testcases/general/infer_map.dart.weak.transformed.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class B extends self::A {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+}
+static field core::List<core::int> intList = core::_GrowableList::_literal2<core::int>(1, 2);
+static field core::List<self::A> list1 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+static method test(core::List<self::A> list) → dynamic {
+  try {
+    list.{core::List::add}(new self::A::•()){(self::A) → void};
+    list.{core::List::removeLast}(){() → self::A};
+  }
+  on core::Object catch(final core::Object e) {
+    return;
+  }
+  throw "Expected subtype error";
+}
+static method main() → dynamic {
+  self::test(self::list1);
+  self::list1 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+  self::test(self::list1);
+  core::List<self::A> list2 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+  self::test(list2);
+  list2 = self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>};
+  self::test(list2);
+  self::test(self::intList.{core::Iterable::map}<self::B>((core::int i) → self::B => new self::B::•()){((core::int) → self::B) → core::Iterable<self::B>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<self::B>});
+}
diff --git a/pkg/kernel/lib/transformations/value_class.dart b/pkg/kernel/lib/transformations/value_class.dart
index f69a386..67fada2 100644
--- a/pkg/kernel/lib/transformations/value_class.dart
+++ b/pkg/kernel/lib/transformations/value_class.dart
@@ -141,8 +141,10 @@
             ..parent = syntheticConstructor)
       .toList();
 
-  initializersConstructor.add(SuperInitializer(superConstructor,
-      Arguments(superParameters.map((f) => VariableGet(f)).toList()))
+  initializersConstructor.add(SuperInitializer(
+      superConstructor,
+      Arguments(
+          superParameters.map<Expression>((f) => VariableGet(f)).toList()))
     ..parent = syntheticConstructor);
 
   syntheticConstructor.function.namedParameters
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index 7a67f89..487d337 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -231,7 +231,9 @@
           _fixedLengthList(
               coreTypes,
               coreTypes.typeLegacyRawType,
-              arguments.types.map((t) => new TypeLiteral(t)).toList(),
+              arguments.types
+                  .map<Expression>((t) => new TypeLiteral(t))
+                  .toList(),
               arguments.fileOffset),
           _fixedLengthList(coreTypes, const DynamicType(), arguments.positional,
               arguments.fileOffset),