[cfe] Add basic support for pattern-for-in loops

Part of https://github.com/dart-lang/sdk/issues/49749

Change-Id: I7becfa2ef558fb483716d7d1bc195d858cc69094
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/281660
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Chloe Stefantsova 2023-02-08 15:54:19 +00:00 committed by Commit Queue
parent a66dcf1b77
commit 85c2bd891b
13 changed files with 621 additions and 7 deletions

View file

@ -6980,10 +6980,34 @@ class BodyBuilder extends StackListenerImpl
@override
void handleForInLoopParts(Token? awaitToken, Token forToken,
Token leftParenthesis, Token? patternKeyword, Token inKeyword) {
if (patternKeyword != null) {
throw new UnimplementedError(
'TODO(paulberry): handle pattern in for-in loop');
debugEvent("ForIntLoopParts");
assert(checkState(forToken, [
unionOfKinds([
ValueKinds.Expression,
ValueKinds.Generator,
ValueKinds.ProblemBuilder,
]),
unionOfKinds([
ValueKinds.Expression,
ValueKinds.Generator,
ValueKinds.ProblemBuilder,
ValueKinds.Pattern,
ValueKinds.Statement, // Variable for non-pattern for-in loop.
]),
]));
Object expression = pop() as Object;
Object pattern = pop() as Object;
if (pattern is Pattern) {
pop(); // Metadata.
for (VariableDeclaration variable in pattern.declaredVariables) {
declareVariable(variable, scope);
typeInferrer.assignedVariables.declare(variable);
}
}
push(pattern);
push(expression);
push(awaitToken ?? NullValues.AwaitToken);
push(forToken);
push(inKeyword);
@ -7090,6 +7114,21 @@ class BodyBuilder extends StackListenerImpl
new VariableGetImpl(variable, forNullGuardedAccess: false)
..fileOffset = inToken.offset,
voidContext: true);
} else if (lvalue is Pattern) {
/// We are in the case where `lvalue` is a pattern:
///
/// for (pattern in expression) body
///
/// This is normalized to:
///
/// for (final #t in expression) {
/// pattern = #t;
/// body;
/// }
elements.syntheticAssignment = null;
elements.expressionEffects = new PatternVariableDeclaration(
lvalue, new VariableGetImpl(variable, forNullGuardedAccess: false),
fileOffset: inToken.offset, isFinal: false);
} else {
Message message = forest.isVariablesDeclaration(lvalue)
? fasta.messageForInLoopExactlyOneVariable
@ -7123,6 +7162,24 @@ class BodyBuilder extends StackListenerImpl
@override
void endForIn(Token endToken) {
debugEvent("ForIn");
assert(checkState(endToken, [
/* body= */ ValueKinds.Statement,
/* inKeyword = */ ValueKinds.Token,
/* forToken = */ ValueKinds.Token,
/* awaitToken = */ ValueKinds.AwaitTokenOrNull,
/* expression = */ unionOfKinds([
ValueKinds.Expression,
ValueKinds.Generator,
ValueKinds.ProblemBuilder,
]),
/* lvalue = */ unionOfKinds([
ValueKinds.Expression,
ValueKinds.Generator,
ValueKinds.ProblemBuilder,
ValueKinds.Pattern,
ValueKinds.Statement,
]),
]));
Statement body = popStatement();
Token inKeyword = pop() as Token;

View file

@ -60,6 +60,8 @@ class ValueKinds {
const SingleValueKind<type.AsyncMarker>();
static const ValueKind AsyncModifier =
const SingleValueKind<type.AsyncMarker>();
static const ValueKind AwaitTokenOrNull =
const SingleValueKind<type.Token>(NullValues.AwaitToken);
static const ValueKind BreakTarget =
const SingleValueKind<type.JumpTarget>(NullValues.BreakTarget);
static const ValueKind Bool = const SingleValueKind<bool>();

View file

@ -71,6 +71,24 @@ class LocalForInVariable implements ForInVariable {
}
}
class PatternVariableDeclarationForInVariable implements ForInVariable {
PatternVariableDeclaration patternVariableDeclaration;
PatternVariableDeclarationForInVariable(this.patternVariableDeclaration);
@override
DartType computeElementType(InferenceVisitorBase visitor) {
return (patternVariableDeclaration.initializer as VariableGet)
.variable
.type;
}
@override
Expression? inferAssignment(InferenceVisitorBase visitor, DartType rhsType) {
return null;
}
}
class PropertyForInVariable implements ForInVariable {
final PropertySet propertySet;

View file

@ -1517,8 +1517,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase
return new ExpressionInferenceResult(inferredType, iterable);
}
ForInVariable computeForInVariable(
Expression? syntheticAssignment, bool hasProblem) {
ForInVariable computeForInVariable(Expression? syntheticAssignment,
Statement? expressionEffects, bool hasProblem) {
if (syntheticAssignment is VariableSet) {
return new LocalForInVariable(syntheticAssignment);
} else if (syntheticAssignment is PropertySet) {
@ -1531,6 +1531,9 @@ class InferenceVisitorImpl extends InferenceVisitorBase
return new StaticForInVariable(syntheticAssignment);
} else if (syntheticAssignment is InvalidExpression || hasProblem) {
return new InvalidForInVariable(syntheticAssignment);
} else if (syntheticAssignment == null &&
expressionEffects is PatternVariableDeclaration) {
return new PatternVariableDeclarationForInVariable(expressionEffects);
} else {
UriOffset uriOffset = _computeUriOffset(syntheticAssignment!);
return problems.unhandled(
@ -1551,8 +1554,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase
required bool hasProblem}) {
// ignore: unnecessary_null_comparison
assert(hasProblem != null);
ForInVariable forInVariable =
computeForInVariable(syntheticAssignment, hasProblem);
ForInVariable forInVariable = computeForInVariable(
syntheticAssignment, expressionEffects, hasProblem);
DartType elementType = forInVariable.computeElementType(this);
ExpressionInferenceResult iterableResult =
inferForInIterable(iterable, elementType, isAsync: isAsync);

View file

@ -0,0 +1,50 @@
test1(dynamic x) {
for (var [int y] in x) {
if (y % 10 == 0) {
return y;
}
}
return -1;
}
test2(Iterable<List<String>> x) {
for (var [..., String y] in x) {
if (y.startsWith("f")) {
return y;
}
}
return "";
}
test3(Iterable<dynamic> x) {
for (var [int y, ...] in x) {
return y;
}
return -1;
}
main() {
expectEquals(test1([[1], [2], [3]]), -1);
expectEquals(test1([[1], [2], [30]]), 30);
expectEquals(test2([["foo", "bar", "baz"], ["bar", "foo", "baz"], ["bar", "baz", "foo"]]), "foo");
expectEquals(test2([]), "");
expectThrows(() => test3([[null, 1, 2]]));
expectEquals(test3([]), -1);
}
expectEquals(x, y) {
if (x != y) {
throw "Expected '${x}' to be equal to '${y}'.";
}
}
expectThrows(void Function() f) {
bool hasThrown = true;
try {
f();
hasThrown = false;
} catch (e) {}
if (!hasThrown) {
throw "Expected function to throw.";
}
}

View file

@ -0,0 +1,78 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic {
for (final dynamic #t1 in x as{TypeError,ForDynamic,ForNonNullableByDefault} core::Iterable<dynamic>) {
core::int y;
{
final dynamic #0#0 = #t1;
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t2 = y = #0#6{core::int} in true))))
throw new _in::ReachabilityError::•();
}
if(y.{core::num::%}(10){(core::num) → core::int} =={core::num::==}{(core::Object) → core::bool} 0) {
return y;
}
}
return 1.{core::int::unary-}(){() → core::int};
}
static method test2(core::Iterable<core::List<core::String>> x) → dynamic {
for (final core::List<core::String> #t3 in x) {
core::String y;
{
final dynamic #0#0 = #t3;
late final core::int #0#2 = #0#0{core::List<dynamic>}.{core::List::length}{core::int};
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(#0#2.{core::num::-}(1){(core::num) → core::int}){(core::int) → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#2.{core::num::>=}(#C1){(core::num) → core::bool} && (#0#6 is{ForNonNullableByDefault} core::String && (let final dynamic #t4 = y = #0#6{core::String} in true))))
throw new _in::ReachabilityError::•();
}
if(y.{core::String::startsWith}("f"){(core::Pattern, [core::int]) → core::bool}) {
return y;
}
}
return "";
}
static method test3(core::Iterable<dynamic> x) → dynamic {
for (final dynamic #t5 in x) {
core::int y;
{
final dynamic #0#0 = #t5;
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(#C1){(core::num) → core::bool} && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t6 = y = #0#6{core::int} in true))))
throw new _in::ReachabilityError::•();
}
return y;
}
return 1.{core::int::unary-}(){() → core::int};
}
static method main() → dynamic {
self::expectEquals(self::test1(<core::List<core::int>>[<core::int>[1], <core::int>[2], <core::int>[3]]), 1.{core::int::unary-}(){() → core::int});
self::expectEquals(self::test1(<core::List<core::int>>[<core::int>[1], <core::int>[2], <core::int>[30]]), 30);
self::expectEquals(self::test2(<core::List<core::String>>[<core::String>["foo", "bar", "baz"], <core::String>["bar", "foo", "baz"], <core::String>["bar", "baz", "foo"]]), "foo");
self::expectEquals(self::test2(<core::List<core::String>>[]), "");
self::expectThrows(() → void => self::test3(<dynamic>[<core::int?>[null, 1, 2]]));
self::expectEquals(self::test3(<dynamic>[]), 1.{core::int::unary-}(){() → core::int});
}
static method expectEquals(dynamic x, dynamic y) → dynamic {
if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
throw "Expected '${x}' to be equal to '${y}'.";
}
}
static method expectThrows(() → void f) → dynamic {
core::bool hasThrown = true;
try {
f(){() → void};
hasThrown = false;
}
on core::Object catch(final core::Object e) {
}
if(!hasThrown) {
throw "Expected function to throw.";
}
}
constants {
#C1 = 1
}

View file

@ -0,0 +1,111 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic {
{
core::Iterator<dynamic> :sync-for-iterator = (x as{TypeError,ForDynamic,ForNonNullableByDefault} core::Iterable<dynamic>).{core::Iterable::iterator}{core::Iterator<dynamic>};
for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
final dynamic #t1 = :sync-for-iterator.{core::Iterator::current}{dynamic};
{
core::int y;
{
final dynamic #0#0 = #t1;
function ##0#6#initializer() → dynamic
return #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
late final dynamic #0#6 = ##0#6#initializer(){() → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (#0#6 is{ForNonNullableByDefault} core::int && (let final core::int #t2 = y = #0#6{core::int} in true))))
throw new _in::ReachabilityError::•();
}
if(y.{core::num::%}(10){(core::num) → core::int} =={core::num::==}{(core::Object) → core::bool} 0) {
return y;
}
}
}
}
return 1.{core::int::unary-}(){() → core::int};
}
static method test2(core::Iterable<core::List<core::String>> x) → dynamic {
{
core::Iterator<core::List<core::String>> :sync-for-iterator = x.{core::Iterable::iterator}{core::Iterator<core::List<core::String>>};
for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
final core::List<core::String> #t3 = :sync-for-iterator.{core::Iterator::current}{core::List<core::String>};
{
core::String y;
{
final dynamic #0#0 = #t3;
function ##0#2#initializer() → core::int
return #0#0{core::List<dynamic>}.{core::List::length}{core::int};
late final core::int #0#2 = ##0#2#initializer(){() → core::int};
function ##0#6#initializer() → dynamic
return #0#0{core::List<dynamic>}.{core::List::[]}(#0#2.{core::num::-}(1){(core::num) → core::int}){(core::int) → dynamic};
late final dynamic #0#6 = ##0#6#initializer(){() → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#2.{core::num::>=}(#C1){(core::num) → core::bool} && (#0#6 is{ForNonNullableByDefault} core::String && (let final core::String #t4 = y = #0#6{core::String} in true))))
throw new _in::ReachabilityError::•();
}
if(y.{core::String::startsWith}("f"){(core::Pattern, [core::int]) → core::bool}) {
return y;
}
}
}
}
return "";
}
static method test3(core::Iterable<dynamic> x) → dynamic {
{
core::Iterator<dynamic> :sync-for-iterator = x.{core::Iterable::iterator}{core::Iterator<dynamic>};
for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
final dynamic #t5 = :sync-for-iterator.{core::Iterator::current}{dynamic};
{
core::int y;
{
final dynamic #0#0 = #t5;
function ##0#6#initializer() → dynamic
return #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
late final dynamic #0#6 = ##0#6#initializer(){() → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(#C1){(core::num) → core::bool} && (#0#6 is{ForNonNullableByDefault} core::int && (let final core::int #t6 = y = #0#6{core::int} in true))))
throw new _in::ReachabilityError::•();
}
return y;
}
}
}
return 1.{core::int::unary-}(){() → core::int};
}
static method main() → dynamic {
self::expectEquals(self::test1(core::_GrowableList::_literal3<core::List<core::int>>(core::_GrowableList::_literal1<core::int>(1), core::_GrowableList::_literal1<core::int>(2), core::_GrowableList::_literal1<core::int>(3))), 1.{core::int::unary-}(){() → core::int});
self::expectEquals(self::test1(core::_GrowableList::_literal3<core::List<core::int>>(core::_GrowableList::_literal1<core::int>(1), core::_GrowableList::_literal1<core::int>(2), core::_GrowableList::_literal1<core::int>(30))), 30);
self::expectEquals(self::test2(core::_GrowableList::_literal3<core::List<core::String>>(core::_GrowableList::_literal3<core::String>("foo", "bar", "baz"), core::_GrowableList::_literal3<core::String>("bar", "foo", "baz"), core::_GrowableList::_literal3<core::String>("bar", "baz", "foo"))), "foo");
self::expectEquals(self::test2(core::_GrowableList::•<core::List<core::String>>(0)), "");
self::expectThrows(() → void => self::test3(core::_GrowableList::_literal1<dynamic>(core::_GrowableList::_literal3<core::int?>(null, 1, 2))));
self::expectEquals(self::test3(core::_GrowableList::•<dynamic>(0)), 1.{core::int::unary-}(){() → core::int});
}
static method expectEquals(dynamic x, dynamic y) → dynamic {
if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
throw "Expected '${x}' to be equal to '${y}'.";
}
}
static method expectThrows(() → void f) → dynamic {
core::bool hasThrown = true;
try {
f(){() → void};
hasThrown = false;
}
on core::Object catch(final core::Object e) {
}
if(!hasThrown) {
throw "Expected function to throw.";
}
}
constants {
#C1 = 1
}
Extra constant evaluation status:
Evaluated: InstanceInvocation @ org-dartlang-testcase:///simple_pattern_for_in.dart:7:10 -> IntConstant(-1)
Evaluated: InstanceInvocation @ org-dartlang-testcase:///simple_pattern_for_in.dart:23:10 -> IntConstant(-1)
Evaluated: InstanceInvocation @ org-dartlang-testcase:///simple_pattern_for_in.dart:27:40 -> IntConstant(-1)
Evaluated: InstanceInvocation @ org-dartlang-testcase:///simple_pattern_for_in.dart:32:27 -> IntConstant(-1)
Extra constant evaluation: evaluated: 138, effectively constant: 4

View file

@ -0,0 +1,6 @@
test1(dynamic x) {}
test2(Iterable<List<String>> x) {}
test3(Iterable<dynamic> x) {}
main() {}
expectEquals(x, y) {}
expectThrows(void Function() f) {}

View file

@ -0,0 +1,6 @@
expectEquals(x, y) {}
expectThrows(void Function() f) {}
main() {}
test1(dynamic x) {}
test2(Iterable<List<String>> x) {}
test3(Iterable<dynamic> x) {}

View file

@ -0,0 +1,78 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic {
for (final dynamic #t1 in x as{TypeError,ForDynamic,ForNonNullableByDefault} core::Iterable<dynamic>) {
core::int y;
{
final dynamic #0#0 = #t1;
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t2 = y = #0#6{core::int} in true))))
throw new _in::ReachabilityError::•();
}
if(y.{core::num::%}(10){(core::num) → core::int} =={core::num::==}{(core::Object) → core::bool} 0) {
return y;
}
}
return 1.{core::int::unary-}(){() → core::int};
}
static method test2(core::Iterable<core::List<core::String>> x) → dynamic {
for (final core::List<core::String> #t3 in x) {
core::String y;
{
final dynamic #0#0 = #t3;
late final core::int #0#2 = #0#0{core::List<dynamic>}.{core::List::length}{core::int};
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(#0#2.{core::num::-}(1){(core::num) → core::int}){(core::int) → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#2.{core::num::>=}(#C1){(core::num) → core::bool} && (#0#6 is{ForNonNullableByDefault} core::String && (let final dynamic #t4 = y = #0#6{core::String} in true))))
throw new _in::ReachabilityError::•();
}
if(y.{core::String::startsWith}("f"){(core::Pattern, [core::int]) → core::bool}) {
return y;
}
}
return "";
}
static method test3(core::Iterable<dynamic> x) → dynamic {
for (final dynamic #t5 in x) {
core::int y;
{
final dynamic #0#0 = #t5;
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(#C1){(core::num) → core::bool} && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t6 = y = #0#6{core::int} in true))))
throw new _in::ReachabilityError::•();
}
return y;
}
return 1.{core::int::unary-}(){() → core::int};
}
static method main() → dynamic {
self::expectEquals(self::test1(<core::List<core::int>>[<core::int>[1], <core::int>[2], <core::int>[3]]), 1.{core::int::unary-}(){() → core::int});
self::expectEquals(self::test1(<core::List<core::int>>[<core::int>[1], <core::int>[2], <core::int>[30]]), 30);
self::expectEquals(self::test2(<core::List<core::String>>[<core::String>["foo", "bar", "baz"], <core::String>["bar", "foo", "baz"], <core::String>["bar", "baz", "foo"]]), "foo");
self::expectEquals(self::test2(<core::List<core::String>>[]), "");
self::expectThrows(() → void => self::test3(<dynamic>[<core::int?>[null, 1, 2]]));
self::expectEquals(self::test3(<dynamic>[]), 1.{core::int::unary-}(){() → core::int});
}
static method expectEquals(dynamic x, dynamic y) → dynamic {
if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
throw "Expected '${x}' to be equal to '${y}'.";
}
}
static method expectThrows(() → void f) → dynamic {
core::bool hasThrown = true;
try {
f(){() → void};
hasThrown = false;
}
on core::Object catch(final core::Object e) {
}
if(!hasThrown) {
throw "Expected function to throw.";
}
}
constants {
#C1 = 1
}

View file

@ -0,0 +1,78 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic {
for (final dynamic #t1 in x as{TypeError,ForDynamic,ForNonNullableByDefault} core::Iterable<dynamic>) {
core::int y;
{
final dynamic #0#0 = #t1;
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t2 = y = #0#6{core::int} in true))))
throw new _in::ReachabilityError::•();
}
if(y.{core::num::%}(10){(core::num) → core::int} =={core::num::==}{(core::Object) → core::bool} 0) {
return y;
}
}
return 1.{core::int::unary-}(){() → core::int};
}
static method test2(core::Iterable<core::List<core::String>> x) → dynamic {
for (final core::List<core::String> #t3 in x) {
core::String y;
{
final dynamic #0#0 = #t3;
late final core::int #0#2 = #0#0{core::List<dynamic>}.{core::List::length}{core::int};
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(#0#2.{core::num::-}(1){(core::num) → core::int}){(core::int) → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#2.{core::num::>=}(#C1){(core::num) → core::bool} && (#0#6 is{ForNonNullableByDefault} core::String && (let final dynamic #t4 = y = #0#6{core::String} in true))))
throw new _in::ReachabilityError::•();
}
if(y.{core::String::startsWith}("f"){(core::Pattern, [core::int]) → core::bool}) {
return y;
}
}
return "";
}
static method test3(core::Iterable<dynamic> x) → dynamic {
for (final dynamic #t5 in x) {
core::int y;
{
final dynamic #0#0 = #t5;
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(#C1){(core::num) → core::bool} && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t6 = y = #0#6{core::int} in true))))
throw new _in::ReachabilityError::•();
}
return y;
}
return 1.{core::int::unary-}(){() → core::int};
}
static method main() → dynamic {
self::expectEquals(self::test1(<core::List<core::int>>[<core::int>[1], <core::int>[2], <core::int>[3]]), 1.{core::int::unary-}(){() → core::int});
self::expectEquals(self::test1(<core::List<core::int>>[<core::int>[1], <core::int>[2], <core::int>[30]]), 30);
self::expectEquals(self::test2(<core::List<core::String>>[<core::String>["foo", "bar", "baz"], <core::String>["bar", "foo", "baz"], <core::String>["bar", "baz", "foo"]]), "foo");
self::expectEquals(self::test2(<core::List<core::String>>[]), "");
self::expectThrows(() → void => self::test3(<dynamic>[<core::int?>[null, 1, 2]]));
self::expectEquals(self::test3(<dynamic>[]), 1.{core::int::unary-}(){() → core::int});
}
static method expectEquals(dynamic x, dynamic y) → dynamic {
if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
throw "Expected '${x}' to be equal to '${y}'.";
}
}
static method expectThrows(() → void f) → dynamic {
core::bool hasThrown = true;
try {
f(){() → void};
hasThrown = false;
}
on core::Object catch(final core::Object e) {
}
if(!hasThrown) {
throw "Expected function to throw.";
}
}
constants {
#C1 = 1
}

View file

@ -0,0 +1,16 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
static method test1(dynamic x) → dynamic
;
static method test2(core::Iterable<core::List<core::String>> x) → dynamic
;
static method test3(core::Iterable<dynamic> x) → dynamic
;
static method main() → dynamic
;
static method expectEquals(dynamic x, dynamic y) → dynamic
;
static method expectThrows(() → void f) → dynamic
;

View file

@ -0,0 +1,111 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic {
{
core::Iterator<dynamic> :sync-for-iterator = (x as{TypeError,ForDynamic,ForNonNullableByDefault} core::Iterable<dynamic>).{core::Iterable::iterator}{core::Iterator<dynamic>};
for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
final dynamic #t1 = :sync-for-iterator.{core::Iterator::current}{dynamic};
{
core::int y;
{
final dynamic #0#0 = #t1;
function ##0#6#initializer() → dynamic
return #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
late final dynamic #0#6 = ##0#6#initializer(){() → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (#0#6 is{ForNonNullableByDefault} core::int && (let final core::int #t2 = y = #0#6{core::int} in true))))
throw new _in::ReachabilityError::•();
}
if(y.{core::num::%}(10){(core::num) → core::int} =={core::num::==}{(core::Object) → core::bool} 0) {
return y;
}
}
}
}
return 1.{core::int::unary-}(){() → core::int};
}
static method test2(core::Iterable<core::List<core::String>> x) → dynamic {
{
core::Iterator<core::List<core::String>> :sync-for-iterator = x.{core::Iterable::iterator}{core::Iterator<core::List<core::String>>};
for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
final core::List<core::String> #t3 = :sync-for-iterator.{core::Iterator::current}{core::List<core::String>};
{
core::String y;
{
final dynamic #0#0 = #t3;
function ##0#2#initializer() → core::int
return #0#0{core::List<dynamic>}.{core::List::length}{core::int};
late final core::int #0#2 = ##0#2#initializer(){() → core::int};
function ##0#6#initializer() → dynamic
return #0#0{core::List<dynamic>}.{core::List::[]}(#0#2.{core::num::-}(1){(core::num) → core::int}){(core::int) → dynamic};
late final dynamic #0#6 = ##0#6#initializer(){() → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#2.{core::num::>=}(#C1){(core::num) → core::bool} && (#0#6 is{ForNonNullableByDefault} core::String && (let final core::String #t4 = y = #0#6{core::String} in true))))
throw new _in::ReachabilityError::•();
}
if(y.{core::String::startsWith}("f"){(core::Pattern, [core::int]) → core::bool}) {
return y;
}
}
}
}
return "";
}
static method test3(core::Iterable<dynamic> x) → dynamic {
{
core::Iterator<dynamic> :sync-for-iterator = x.{core::Iterable::iterator}{core::Iterator<dynamic>};
for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
final dynamic #t5 = :sync-for-iterator.{core::Iterator::current}{dynamic};
{
core::int y;
{
final dynamic #0#0 = #t5;
function ##0#6#initializer() → dynamic
return #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
late final dynamic #0#6 = ##0#6#initializer(){() → dynamic};
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(#C1){(core::num) → core::bool} && (#0#6 is{ForNonNullableByDefault} core::int && (let final core::int #t6 = y = #0#6{core::int} in true))))
throw new _in::ReachabilityError::•();
}
return y;
}
}
}
return 1.{core::int::unary-}(){() → core::int};
}
static method main() → dynamic {
self::expectEquals(self::test1(core::_GrowableList::_literal3<core::List<core::int>>(core::_GrowableList::_literal1<core::int>(1), core::_GrowableList::_literal1<core::int>(2), core::_GrowableList::_literal1<core::int>(3))), 1.{core::int::unary-}(){() → core::int});
self::expectEquals(self::test1(core::_GrowableList::_literal3<core::List<core::int>>(core::_GrowableList::_literal1<core::int>(1), core::_GrowableList::_literal1<core::int>(2), core::_GrowableList::_literal1<core::int>(30))), 30);
self::expectEquals(self::test2(core::_GrowableList::_literal3<core::List<core::String>>(core::_GrowableList::_literal3<core::String>("foo", "bar", "baz"), core::_GrowableList::_literal3<core::String>("bar", "foo", "baz"), core::_GrowableList::_literal3<core::String>("bar", "baz", "foo"))), "foo");
self::expectEquals(self::test2(core::_GrowableList::•<core::List<core::String>>(0)), "");
self::expectThrows(() → void => self::test3(core::_GrowableList::_literal1<dynamic>(core::_GrowableList::_literal3<core::int?>(null, 1, 2))));
self::expectEquals(self::test3(core::_GrowableList::•<dynamic>(0)), 1.{core::int::unary-}(){() → core::int});
}
static method expectEquals(dynamic x, dynamic y) → dynamic {
if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
throw "Expected '${x}' to be equal to '${y}'.";
}
}
static method expectThrows(() → void f) → dynamic {
core::bool hasThrown = true;
try {
f(){() → void};
hasThrown = false;
}
on core::Object catch(final core::Object e) {
}
if(!hasThrown) {
throw "Expected function to throw.";
}
}
constants {
#C1 = 1
}
Extra constant evaluation status:
Evaluated: InstanceInvocation @ org-dartlang-testcase:///simple_pattern_for_in.dart:7:10 -> IntConstant(-1)
Evaluated: InstanceInvocation @ org-dartlang-testcase:///simple_pattern_for_in.dart:23:10 -> IntConstant(-1)
Evaluated: InstanceInvocation @ org-dartlang-testcase:///simple_pattern_for_in.dart:27:40 -> IntConstant(-1)
Evaluated: InstanceInvocation @ org-dartlang-testcase:///simple_pattern_for_in.dart:32:27 -> IntConstant(-1)
Extra constant evaluation: evaluated: 138, effectively constant: 4