[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>
This commit is contained in:
Johnni Winther 2022-04-20 06:56:58 +00:00 committed by Commit Bot
parent 35c78ac66b
commit f26c0e2056
12 changed files with 192 additions and 10 deletions

View file

@ -179,7 +179,7 @@ class JsUtilOptimizer extends Transformer {
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 @@ class JsUtilOptimizer extends Transformer {
}
// 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 @@ class JsUtilOptimizer extends Transformer {
return node;
}
callUncheckedArguments = argumentsListConstant.entries
.map((constant) => ConstantExpression(
.map<Expression>((constant) => ConstantExpression(
constant, constant.getType(_staticTypeContext)))
.toList();
entryType = argumentsListConstant.typeArgument;

View file

@ -191,8 +191,9 @@ class Dart2jsTarget extends Target {
.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) {

View file

@ -92,7 +92,7 @@ class RedirectingFactoryBody extends ReturnStatement {
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(

View file

@ -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());
}

View file

@ -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() {}

View file

@ -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) {}

View file

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

View file

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

View file

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

View file

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

View file

@ -141,8 +141,10 @@ void addConstructor(
..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

View file

@ -231,7 +231,9 @@ class VmTarget extends Target {
_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),