[cfe] Desugar pattern variable assignments

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

Change-Id: Ibae19d0e64f023aea047b007d73f3cee9910d259
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/277683
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Chloe Stefantsova 2022-12-30 15:44:37 +00:00 committed by Commit Queue
parent 8e07d492dd
commit 9383820f15
14 changed files with 649 additions and 7 deletions

View file

@ -8483,8 +8483,17 @@ class BodyBuilder extends StackListenerImpl
Pattern pattern;
if (variable.lexeme == "_") {
pattern = new WildcardPattern(patternType, variable.charOffset);
} else if (inAssignmentPattern) {
Expression variableUse =
toValue(scopeLookup(scope, variable.lexeme, variable));
if (variableUse is VariableGet) {
pattern = new AssignedVariablePattern(variableUse.variable,
offset: variable.charOffset);
} else {
// Recover by using [WildcardPattern] instead.
pattern = new WildcardPattern(patternType, variable.charOffset);
}
} else {
// TODO(paulberry): use inAssignmentPattern.
pattern = new VariablePattern(
patternType,
variable.lexeme,
@ -8552,7 +8561,24 @@ class BodyBuilder extends StackListenerImpl
// TODO(johnniwinther,cstefantsova): Handle metadata.
pop(NullValue.Metadata) as List<Expression>?;
push(new PatternVariableDeclaration(pattern, initializer,
offset: keyword.charOffset, isFinal: isFinal));
fileOffset: keyword.charOffset, isFinal: isFinal));
}
@override
void handlePatternAssignment(Token equals) {
debugEvent("PatternAssignment");
assert(checkState(equals, [
unionOfKinds([
ValueKinds.Expression,
ValueKinds.Generator,
ValueKinds.ProblemBuilder,
]),
ValueKinds.Pattern
]));
Expression expression = popForValue();
Pattern pattern = toPattern(pop());
push(new PatternAssignment(pattern, expression,
fileOffset: equals.charOffset));
}
}

View file

@ -386,6 +386,14 @@ class Forest {
..fileEndOffset = fileEndOffset;
}
/// Return a representation of a block expression.
BlockExpression createBlockExpression(
int fileOffset, Block body, Expression value) {
// ignore: unnecessary_null_comparison
assert(fileOffset != null);
return new BlockExpression(body, value)..fileOffset = fileOffset;
}
/// Return a representation of a break statement.
Statement createBreakStatement(int fileOffset, Object? label) {
// ignore: unnecessary_null_comparison

View file

@ -5020,8 +5020,8 @@ class PatternVariableDeclaration extends InternalStatement {
final bool isFinal;
PatternVariableDeclaration(this.pattern, this.initializer,
{required int offset, required this.isFinal}) {
fileOffset = offset;
{required int fileOffset, required this.isFinal}) {
super.fileOffset = fileOffset;
}
@override
@ -5048,6 +5048,72 @@ class PatternVariableDeclaration extends InternalStatement {
}
}
class PatternAssignment extends InternalExpression {
final Pattern pattern;
final Expression expression;
PatternAssignment(this.pattern, this.expression, {required int fileOffset}) {
super.fileOffset = fileOffset;
}
@override
ExpressionInferenceResult acceptInference(
InferenceVisitorImpl visitor, DartType typeContext) {
return visitor.visitPatternAssignment(this, typeContext);
}
@override
String toString() {
return "PatternAssignment(${toStringInternal()})";
}
}
class AssignedVariablePattern extends Pattern {
final VariableDeclaration variable;
AssignedVariablePattern(this.variable, {required int offset}) : super(offset);
@override
PatternInferenceResult acceptInference(InferenceVisitorImpl visitor,
{required SharedMatchContext context}) {
return visitor.visitAssignedVariablePattern(this, context: context);
}
@override
List<VariableDeclaration> get declaredVariables => [variable];
@override
void toTextInternal(AstPrinter printer) {
printer.write(variable.name!);
}
@override
String toString() {
return "AssignedVariablePattern(${toStringInternal()})";
}
@override
PatternTransformationResult transform(
Expression matchedExpression,
DartType matchedType,
Expression variableInitializingContext,
InferenceVisitorBase inferenceVisitor) {
// condition: let _ = `variable` = `matchedExpression` in true
return new PatternTransformationResult([
new PatternTransformationElement(
kind: PatternTransformationElementKind.regular,
condition: inferenceVisitor.engine.forest.createLet(
inferenceVisitor.engine.forest.createVariableDeclarationForValue(
inferenceVisitor.engine.forest.createVariableSet(
fileOffset, variable, matchedExpression)),
inferenceVisitor.engine.forest
.createBoolLiteral(fileOffset, true))
..fileOffset = fileOffset,
variableInitializers: [])
]);
}
}
final Pattern dummyPattern = new ExpressionPattern(dummyExpression);
/// Internal statement for a if-case statements:
@ -5617,7 +5683,7 @@ class VariablePattern extends Pattern {
@override
String toString() {
return "VariableBinder(${toStringInternal()})";
return "VariablePattern(${toStringInternal()})";
}
}

View file

@ -9304,6 +9304,82 @@ class InferenceVisitorImpl extends InferenceVisitorBase
return const PatternInferenceResult();
}
ExpressionInferenceResult visitPatternAssignment(
PatternAssignment node, DartType typeContext) {
Expression expression = node.expression;
ExpressionTypeAnalysisResult<DartType> analysisResult =
analyzePatternAssignment(node, node.pattern, expression);
Node? rewrite = popRewrite();
if (!identical(expression, rewrite)) {
expression = rewrite as Expression;
}
// TODO(cstefantsova): Do we need a more precise type for the variable?
VariableDeclaration matchedExpressionVariable = engine.forest
.createVariableDeclarationForValue(expression,
type: const DynamicType());
VariableDeclaration isPatternMatchingFailed = engine.forest
.createVariableDeclarationForValue(
engine.forest.createBoolLiteral(node.fileOffset, true),
type: coreTypes.boolNonNullableRawType);
// patternMatchedSet: `isPatternMatchingFailed` = false;
// ==> VAR = true;
Statement patternMatchedSet = engine.forest.createExpressionStatement(
node.fileOffset,
engine.forest.createVariableSet(
node.fileOffset,
isPatternMatchingFailed,
engine.forest.createBoolLiteral(node.fileOffset, false)));
PatternTransformationResult transformationResult = node.pattern.transform(
engine.forest
.createVariableGet(node.fileOffset, matchedExpressionVariable),
const DynamicType(),
engine.forest
.createVariableGet(node.fileOffset, matchedExpressionVariable),
this);
List<Statement> replacementStatements = _transformationResultToStatements(
node.fileOffset, transformationResult, [patternMatchedSet]);
replacementStatements = [
matchedExpressionVariable,
isPatternMatchingFailed,
...replacementStatements,
// TODO(cstefantsova): Figure out the right exception to throw.
engine.forest.createIfStatement(
node.fileOffset,
engine.forest
.createVariableGet(node.fileOffset, isPatternMatchingFailed),
engine.forest.createExpressionStatement(
node.fileOffset,
new Throw(
new ConstructorInvocation(
coreTypes.reachabilityErrorConstructor,
engine.forest.createArguments(node.fileOffset, []))
..fileOffset = node.fileOffset)
..fileOffset = node.fileOffset),
null),
];
Expression replacement = engine.forest.createBlockExpression(
node.fileOffset,
engine.forest.createBlock(
node.fileOffset, node.fileOffset, replacementStatements),
engine.forest
.createVariableGet(node.fileOffset, matchedExpressionVariable));
return new ExpressionInferenceResult(
analysisResult.resolveShorting(), replacement);
}
PatternInferenceResult visitAssignedVariablePattern(
AssignedVariablePattern node,
{required SharedMatchContext context}) {
analyzeAssignedVariablePattern(context, node, node.variable);
return const PatternInferenceResult();
}
@override
shared.RecordType<DartType>? asRecordType(DartType type) {
if (type is RecordType) {

View file

@ -1318,14 +1318,14 @@ void _testPatternVariableDeclaration() {
testStatement(
new PatternVariableDeclaration(
new ExpressionPattern(new IntLiteral(0)), new IntLiteral(1),
isFinal: false, offset: TreeNode.noOffset),
isFinal: false, fileOffset: TreeNode.noOffset),
'''
var 0 = 1;''');
testStatement(
new PatternVariableDeclaration(
new ExpressionPattern(new IntLiteral(0)), new IntLiteral(1),
isFinal: true, offset: TreeNode.noOffset),
isFinal: true, fileOffset: TreeNode.noOffset),
'''
final 0 = 1;''');
}

View file

@ -0,0 +1,43 @@
// 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.
test1(dynamic x) {
int a;
[a] = x;
return a;
}
test2(dynamic x) {
int a, b, c;
[c, ...] = [a && b, ...] = x;
return a + b + c;
}
main() {
expectEquals(test1([1]), 1);
expectThrows(() => test1([]));
expectThrows(() => test1([1, 2, 3]));
expectThrows(() => test1("foo"));
expectThrows(() => test1(null));
expectEquals(test2([1]), 3);
expectThrows(() => test2(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 the function to throw.";
}
}

View file

@ -0,0 +1,80 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic {
core::int a;
block {
final dynamic #t1 = x;
final core::bool #t2 = true;
final dynamic #t3 = #t1;
if(#t3 is core::List<dynamic> && #t3{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::==}(1){(core::Object) → core::bool}) {
final dynamic #t4 = #t3{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(let final dynamic #t5 = a = #t4 in true) {
#t2 = false;
}
}
if(#t2)
throw new _in::ReachabilityError::•();
} =>#t1;
return a;
}
static method test2(dynamic x) → dynamic {
core::int a;
core::int b;
core::int c;
block {
final dynamic #t6 = block {
final dynamic #t7 = x;
final core::bool #t8 = true;
final dynamic #t9 = #t7;
if(#t9 is core::List<dynamic> && #t9{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(1){(core::num) → core::bool}) {
final dynamic #t10 = #t9{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
final dynamic #t11 = #t10;
if((let final dynamic #t12 = a = #t11 in true) && (let final dynamic #t13 = b = #t11 in true)) {
#t8 = false;
}
}
if(#t8)
throw new _in::ReachabilityError::•();
} =>#t7;
final core::bool #t14 = true;
final dynamic #t15 = #t6;
if(#t15 is core::List<dynamic> && #t15{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(1){(core::num) → core::bool}) {
final dynamic #t16 = #t15{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(let final dynamic #t17 = c = #t16 in true) {
#t14 = false;
}
}
if(#t14)
throw new _in::ReachabilityError::•();
} =>#t6;
return a.{core::num::+}(b){(core::num) → core::int}.{core::num::+}(c){(core::num) → core::int};
}
static method main() → dynamic {
self::expectEquals(self::test1(<core::int>[1]), 1);
self::expectThrows(() → void => self::test1(<dynamic>[]));
self::expectThrows(() → void => self::test1(<core::int>[1, 2, 3]));
self::expectThrows(() → void => self::test1("foo"));
self::expectThrows(() → void => self::test1(null));
self::expectEquals(self::test2(<core::int>[1]), 3);
self::expectThrows(() → void => self::test2(1));
}
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 the function to throw.";
}
}

View file

@ -0,0 +1,80 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic {
core::int a;
block {
final dynamic #t1 = x;
final core::bool #t2 = true;
final dynamic #t3 = #t1;
if(#t3 is core::List<dynamic> && #t3{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::==}(1){(core::Object) → core::bool}) {
final dynamic #t4 = #t3{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(let final dynamic #t5 = a = #t4 in true) {
#t2 = false;
}
}
if(#t2)
throw new _in::ReachabilityError::•();
} =>#t1;
return a;
}
static method test2(dynamic x) → dynamic {
core::int a;
core::int b;
core::int c;
block {
final dynamic #t6 = block {
final dynamic #t7 = x;
final core::bool #t8 = true;
final dynamic #t9 = #t7;
if(#t9 is core::List<dynamic> && #t9{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(1){(core::num) → core::bool}) {
final dynamic #t10 = #t9{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
final dynamic #t11 = #t10;
if((let final dynamic #t12 = a = #t11 in true) && (let final dynamic #t13 = b = #t11 in true)) {
#t8 = false;
}
}
if(#t8)
throw new _in::ReachabilityError::•();
} =>#t7;
final core::bool #t14 = true;
final dynamic #t15 = #t6;
if(#t15 is core::List<dynamic> && #t15{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(1){(core::num) → core::bool}) {
final dynamic #t16 = #t15{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(let final dynamic #t17 = c = #t16 in true) {
#t14 = false;
}
}
if(#t14)
throw new _in::ReachabilityError::•();
} =>#t6;
return a.{core::num::+}(b){(core::num) → core::int}.{core::num::+}(c){(core::num) → core::int};
}
static method main() → dynamic {
self::expectEquals(self::test1(core::_GrowableList::_literal1<core::int>(1)), 1);
self::expectThrows(() → void => self::test1(core::_GrowableList::•<dynamic>(0)));
self::expectThrows(() → void => self::test1(core::_GrowableList::_literal3<core::int>(1, 2, 3)));
self::expectThrows(() → void => self::test1("foo"));
self::expectThrows(() → void => self::test1(null));
self::expectEquals(self::test2(core::_GrowableList::_literal1<core::int>(1)), 3);
self::expectThrows(() → void => self::test2(1));
}
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 the function to throw.";
}
}

View file

@ -0,0 +1,5 @@
test1(dynamic x) {}
test2(dynamic x) {}
main() {}
expectEquals(x, y) {}
expectThrows(void Function() f) {}

View file

@ -0,0 +1,5 @@
expectEquals(x, y) {}
expectThrows(void Function() f) {}
main() {}
test1(dynamic x) {}
test2(dynamic x) {}

View file

@ -0,0 +1,80 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic {
core::int a;
block {
final dynamic #t1 = x;
final core::bool #t2 = true;
final dynamic #t3 = #t1;
if(#t3 is core::List<dynamic> && #t3{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::==}(1){(core::Object) → core::bool}) {
final dynamic #t4 = #t3{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(let final dynamic #t5 = a = #t4 in true) {
#t2 = false;
}
}
if(#t2)
throw new _in::ReachabilityError::•();
} =>#t1;
return a;
}
static method test2(dynamic x) → dynamic {
core::int a;
core::int b;
core::int c;
block {
final dynamic #t6 = block {
final dynamic #t7 = x;
final core::bool #t8 = true;
final dynamic #t9 = #t7;
if(#t9 is core::List<dynamic> && #t9{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(1){(core::num) → core::bool}) {
final dynamic #t10 = #t9{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
final dynamic #t11 = #t10;
if((let final dynamic #t12 = a = #t11 in true) && (let final dynamic #t13 = b = #t11 in true)) {
#t8 = false;
}
}
if(#t8)
throw new _in::ReachabilityError::•();
} =>#t7;
final core::bool #t14 = true;
final dynamic #t15 = #t6;
if(#t15 is core::List<dynamic> && #t15{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(1){(core::num) → core::bool}) {
final dynamic #t16 = #t15{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(let final dynamic #t17 = c = #t16 in true) {
#t14 = false;
}
}
if(#t14)
throw new _in::ReachabilityError::•();
} =>#t6;
return a.{core::num::+}(b){(core::num) → core::int}.{core::num::+}(c){(core::num) → core::int};
}
static method main() → dynamic {
self::expectEquals(self::test1(<core::int>[1]), 1);
self::expectThrows(() → void => self::test1(<dynamic>[]));
self::expectThrows(() → void => self::test1(<core::int>[1, 2, 3]));
self::expectThrows(() → void => self::test1("foo"));
self::expectThrows(() → void => self::test1(null));
self::expectEquals(self::test2(<core::int>[1]), 3);
self::expectThrows(() → void => self::test2(1));
}
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 the function to throw.";
}
}

View file

@ -0,0 +1,80 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic {
core::int a;
block {
final dynamic #t1 = x;
final core::bool #t2 = true;
final dynamic #t3 = #t1;
if(#t3 is core::List<dynamic> && #t3{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::==}(1){(core::Object) → core::bool}) {
final dynamic #t4 = #t3{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(let final dynamic #t5 = a = #t4 in true) {
#t2 = false;
}
}
if(#t2)
throw new _in::ReachabilityError::•();
} =>#t1;
return a;
}
static method test2(dynamic x) → dynamic {
core::int a;
core::int b;
core::int c;
block {
final dynamic #t6 = block {
final dynamic #t7 = x;
final core::bool #t8 = true;
final dynamic #t9 = #t7;
if(#t9 is core::List<dynamic> && #t9{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(1){(core::num) → core::bool}) {
final dynamic #t10 = #t9{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
final dynamic #t11 = #t10;
if((let final dynamic #t12 = a = #t11 in true) && (let final dynamic #t13 = b = #t11 in true)) {
#t8 = false;
}
}
if(#t8)
throw new _in::ReachabilityError::•();
} =>#t7;
final core::bool #t14 = true;
final dynamic #t15 = #t6;
if(#t15 is core::List<dynamic> && #t15{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(1){(core::num) → core::bool}) {
final dynamic #t16 = #t15{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(let final dynamic #t17 = c = #t16 in true) {
#t14 = false;
}
}
if(#t14)
throw new _in::ReachabilityError::•();
} =>#t6;
return a.{core::num::+}(b){(core::num) → core::int}.{core::num::+}(c){(core::num) → core::int};
}
static method main() → dynamic {
self::expectEquals(self::test1(<core::int>[1]), 1);
self::expectThrows(() → void => self::test1(<dynamic>[]));
self::expectThrows(() → void => self::test1(<core::int>[1, 2, 3]));
self::expectThrows(() → void => self::test1("foo"));
self::expectThrows(() → void => self::test1(null));
self::expectEquals(self::test2(<core::int>[1]), 3);
self::expectThrows(() → void => self::test2(1));
}
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 the function to throw.";
}
}

View file

@ -0,0 +1,13 @@
library /*isNonNullableByDefault*/;
import self as self;
static method test1(dynamic x) → dynamic
;
static method test2(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,80 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic {
core::int a;
block {
final dynamic #t1 = x;
final core::bool #t2 = true;
final dynamic #t3 = #t1;
if(#t3 is core::List<dynamic> && #t3{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::==}(1){(core::Object) → core::bool}) {
final dynamic #t4 = #t3{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(let final dynamic #t5 = a = #t4 in true) {
#t2 = false;
}
}
if(#t2)
throw new _in::ReachabilityError::•();
} =>#t1;
return a;
}
static method test2(dynamic x) → dynamic {
core::int a;
core::int b;
core::int c;
block {
final dynamic #t6 = block {
final dynamic #t7 = x;
final core::bool #t8 = true;
final dynamic #t9 = #t7;
if(#t9 is core::List<dynamic> && #t9{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(1){(core::num) → core::bool}) {
final dynamic #t10 = #t9{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
final dynamic #t11 = #t10;
if((let final dynamic #t12 = a = #t11 in true) && (let final dynamic #t13 = b = #t11 in true)) {
#t8 = false;
}
}
if(#t8)
throw new _in::ReachabilityError::•();
} =>#t7;
final core::bool #t14 = true;
final dynamic #t15 = #t6;
if(#t15 is core::List<dynamic> && #t15{core::List<dynamic>}.{core::List::length}{core::int}.{core::num::>=}(1){(core::num) → core::bool}) {
final dynamic #t16 = #t15{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
if(let final dynamic #t17 = c = #t16 in true) {
#t14 = false;
}
}
if(#t14)
throw new _in::ReachabilityError::•();
} =>#t6;
return a.{core::num::+}(b){(core::num) → core::int}.{core::num::+}(c){(core::num) → core::int};
}
static method main() → dynamic {
self::expectEquals(self::test1(core::_GrowableList::_literal1<core::int>(1)), 1);
self::expectThrows(() → void => self::test1(core::_GrowableList::•<dynamic>(0)));
self::expectThrows(() → void => self::test1(core::_GrowableList::_literal3<core::int>(1, 2, 3)));
self::expectThrows(() → void => self::test1("foo"));
self::expectThrows(() → void => self::test1(null));
self::expectEquals(self::test2(core::_GrowableList::_literal1<core::int>(1)), 3);
self::expectThrows(() → void => self::test2(1));
}
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 the function to throw.";
}
}