[cfe] Add support for c-style pattern-for elements in lists

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

Change-Id: Iafc0239535dd89d0ff4bfa99bd10cdb3978b42ea
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/284224
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Chloe Stefantsova 2023-02-21 10:26:23 +00:00 committed by Commit Queue
parent 3424cc8db7
commit 3ab2dfaaaa
13 changed files with 505 additions and 6 deletions

View file

@ -4118,12 +4118,33 @@ class BodyBuilder extends StackListenerImpl
@override
void endForControlFlow(Token token) {
assert(checkState(token, <ValueKind>[
/* entry = */ unionOfKinds(<ValueKind>[
ValueKinds.Generator,
ValueKinds.ExpressionOrNull,
ValueKinds.Statement,
ValueKinds.ParserRecovery,
ValueKinds.MapLiteralEntry,
]),
/* update expression count = */ ValueKinds.Integer,
/* left separator = */ ValueKinds.Token,
/* left parenthesis = */ ValueKinds.Token,
/* for keyword = */ ValueKinds.Token,
]));
debugEvent("ForControlFlow");
Object? entry = pop();
int updateExpressionCount = pop() as int;
pop(); // left separator
pop(); // left parenthesis
Token forToken = pop() as Token;
assert(checkState(token, <ValueKind>[
/* updates = */ ...repeatedKind(
unionOfKinds(
<ValueKind>[ValueKinds.Expression, ValueKinds.Generator]),
updateExpressionCount),
/* condition = */ ValueKinds.Statement,
]));
List<Expression> updates = popListForEffect(updateExpressionCount);
Statement conditionStatement = popStatement(); // condition
@ -4142,6 +4163,7 @@ class BodyBuilder extends StackListenerImpl
// This is matched by the call to [beginNode] in
// [handleForInitializerEmptyStatement],
// [handleForInitializerPatternVariableAssignment],
// [handleForInitializerExpressionStatement], and
// [handleForInitializerLocalVariableDeclaration].
AssignedVariablesNodeInfo assignedVariablesNodeInfo =
@ -4169,9 +4191,18 @@ class BodyBuilder extends StackListenerImpl
typeInferrer.assignedVariables.endNode(result);
push(result);
} else {
ForElement result = forest.createForElement(offsetForToken(forToken),
variables, condition, updates, toValue(entry));
typeInferrer.assignedVariables.endNode(result);
TreeNode result;
ForElement forElement = result = forest.createForElement(
offsetForToken(forToken),
variables,
condition,
updates,
toValue(entry));
if (variableOrExpression is PatternVariableDeclaration) {
result = forest.createPatternForElement(
offsetForToken(forToken), variableOrExpression, forElement);
}
typeInferrer.assignedVariables.endNode(forElement);
push(result);
}
}

View file

@ -310,6 +310,16 @@ class Forest {
..fileOffset = fileOffset;
}
PatternForElement createPatternForElement(
int fileOffset,
PatternVariableDeclaration patternVariableDeclaration,
ForElement forElement) {
// ignore: unnecessary_null_comparison
assert(fileOffset != null);
return new PatternForElement(patternVariableDeclaration, forElement)
..fileOffset = fileOffset;
}
ForMapEntry createForMapEntry(
int fileOffset,
List<VariableDeclaration> variables,

View file

@ -4201,9 +4201,9 @@ class WildcardPattern extends Pattern {
}
class PatternVariableDeclaration extends InternalStatement {
final Pattern pattern;
final Expression initializer;
final bool isFinal;
Pattern pattern;
Expression initializer;
bool isFinal;
PatternVariableDeclaration(this.pattern, this.initializer,
{required int fileOffset, required this.isFinal}) {
@ -4914,3 +4914,48 @@ class IfCaseMapEntry extends TreeNode
return "IfCaseMapEntry(${toStringInternal()})";
}
}
class PatternForElement extends InternalExpression with ControlFlowElement {
PatternVariableDeclaration patternVariableDeclaration;
ForElement forElement;
late List<Statement> replacement;
PatternForElement(this.patternVariableDeclaration, this.forElement);
@override
ExpressionInferenceResult acceptInference(
InferenceVisitorImpl visitor, DartType typeContext) {
throw new UnsupportedError("PatternForElement.acceptInference");
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('for (');
for (int index = 0; index < forElement.variables.length; index++) {
if (index > 0) {
printer.write(', ');
}
printer.writeVariableDeclaration(forElement.variables[index],
includeModifiersAndType: index == 0);
}
printer.write('; ');
if (forElement.condition != null) {
printer.writeExpression(forElement.condition!);
}
printer.write('; ');
printer.writeExpressions(forElement.updates);
printer.write(') ');
printer.writeExpression(forElement.body);
}
@override
MapLiteralEntry? toMapLiteralEntry(
void Function(TreeNode from, TreeNode to) onConvertElement) {
throw new UnimplementedError("toMapLiteralEntry");
}
@override
String toString() {
return "PatternForElement(${toStringInternal()})";
}
}

View file

@ -2348,6 +2348,87 @@ class InferenceVisitorImpl extends InferenceVisitorBase
}
flowAnalysis.for_end();
return new ExpressionInferenceResult(bodyResult.inferredType, element);
} else if (element is PatternForElement) {
int? stackBase;
assert(checkStackBase(element, stackBase = stackHeight));
PatternVariableDeclaration patternVariableDeclaration =
element.patternVariableDeclaration;
analyzePatternVariableDeclaration(
patternVariableDeclaration,
patternVariableDeclaration.pattern,
patternVariableDeclaration.initializer,
isFinal: patternVariableDeclaration.isFinal,
isLate: false);
assert(checkStack(element, stackBase, [
/* pattern = */ ValueKinds.Pattern,
/* initializer = */ ValueKinds.Expression,
]));
Object? rewrite = popRewrite(NullValues.Expression);
if (!identical(patternVariableDeclaration.pattern, rewrite)) {
patternVariableDeclaration.pattern = (rewrite as Pattern)
..parent = patternVariableDeclaration;
}
rewrite = popRewrite();
if (!identical(patternVariableDeclaration.initializer, rewrite)) {
patternVariableDeclaration.initializer = (rewrite as Expression)
..parent = patternVariableDeclaration;
}
MatchingCache matchingCache = createMatchingCache();
MatchingExpressionVisitor matchingExpressionVisitor =
new MatchingExpressionVisitor(matchingCache);
// TODO(cstefantsova): Do we need a more precise type for the variable?
DartType matchedType = const DynamicType();
CacheableExpression matchedExpression =
matchingCache.createRootExpression(
patternVariableDeclaration.initializer, matchedType);
DelayedExpression matchingExpression = matchingExpressionVisitor
.visitPattern(patternVariableDeclaration.pattern, matchedExpression);
matchingExpression.registerUse();
Expression readMatchingExpression =
matchingExpression.createExpression(typeSchemaEnvironment);
List<Statement> replacementStatements = [
...matchingCache.declarations,
// TODO(cstefantsova): Figure out the right exception to throw.
createIfStatement(
createNot(readMatchingExpression),
createExpressionStatement(createThrow(createConstructorInvocation(
coreTypes.reachabilityErrorConstructor,
createArguments([], fileOffset: element.fileOffset),
fileOffset: element.fileOffset))),
fileOffset: element.fileOffset),
];
if (replacementStatements.length > 1) {
// If we need local declarations, create a new block to avoid naming
// collision with declarations in the same parent block.
replacementStatements = [
createBlock(replacementStatements, fileOffset: element.fileOffset)
];
}
replacementStatements = [
...patternVariableDeclaration.pattern.declaredVariables,
...replacementStatements,
];
element.replacement = replacementStatements;
ExpressionInferenceResult inferenceResult = inferElement(
element.forElement,
inferredTypeArgument,
inferredSpreadTypes,
inferredConditionTypes);
element.forElement = (inferenceResult.expression as ForElement)
..parent = element;
return new ExpressionInferenceResult(
inferenceResult.inferredType, element);
} else if (element is ForInElement) {
ForInResult result;
if (element.variable.name == null) {
@ -2661,6 +2742,10 @@ class InferenceVisitorImpl extends InferenceVisitorBase
} else if (element is ForElement) {
_translateForElement(element, receiverType, elementType, result, body,
isSet: isSet);
} else if (element is PatternForElement) {
_translatePatternForElement(
element, receiverType, elementType, result, body,
isSet: isSet);
} else if (element is ForInElement) {
_translateForInElement(element, receiverType, elementType, result, body,
isSet: isSet);
@ -2753,6 +2838,30 @@ class InferenceVisitorImpl extends InferenceVisitorBase
body.add(loop);
}
void _translatePatternForElement(
PatternForElement element,
InterfaceType receiverType,
DartType elementType,
VariableDeclaration result,
List<Statement> body,
{required bool isSet}) {
List<Statement> statements = <Statement>[];
_translateElement(
element.forElement.body, receiverType, elementType, result, statements,
isSet: isSet);
Statement loopBody =
statements.length == 1 ? statements.first : _createBlock(statements);
ForStatement loop = _createForStatement(
element.fileOffset,
element.forElement.variables,
element.forElement.condition,
element.forElement.updates,
loopBody);
libraryBuilder.loader.dataForTesting?.registerAlias(element, loop);
body.addAll(element.replacement);
body.add(loop);
}
void _translateForInElement(ForInElement element, InterfaceType receiverType,
DartType elementType, VariableDeclaration result, List<Statement> body,
{required bool isSet}) {

View file

@ -0,0 +1,36 @@
// Copyright (c) 2023, 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) => [for (var [int i, int n] = x; i < n; i++) i];
main() {
expectEquals(
listToString(test1([0, 3])),
listToString([0, 1, 2]),
);
expectEquals(
listToString(test1([0, 0])),
listToString([]),
);
expectThrows(() => test1({}));
}
expectEquals(x, y) {
if (x != y) {
throw "Expected '${x}' to be equal to '${y}'.";
}
}
listToString(List list) => "[${list.map((e) => '${e}').join(',')}]";
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,48 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic
return block {
final core::List<core::int> #t1 = <core::int>[];
core::int i;
core::int n;
{
final dynamic #0#0 = x;
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
late final dynamic #0#7 = #0#0{core::List<dynamic>}.{core::List::[]}(1){(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 = i = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final dynamic #t3 = n = #0#7{core::int} in true))))
throw new _in::ReachabilityError::•();
}
for (core::int i = i, core::int n = n; i.{core::num::<}(n){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int})
#t1.{core::List::add}{Invariant}(i){(core::int) → void};
} =>#t1;
static method main() → dynamic {
self::expectEquals(self::listToString(self::test1(<core::int>[0, 3]) as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<dynamic>), self::listToString(<dynamic>[0, 1, 2]));
self::expectEquals(self::listToString(self::test1(<core::int>[0, 0]) as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<dynamic>), self::listToString(<dynamic>[]));
self::expectThrows(() → void => self::test1(<dynamic, dynamic>{}));
}
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 listToString(core::List<dynamic> list) → dynamic
return "[${list.{core::Iterable::map}<core::String>((dynamic e) → core::String => "${e}"){((dynamic) → core::String) → core::Iterable<core::String>}.{core::Iterable::join}(","){([core::String]) → core::String}}]";
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.";
}
}
constants {
#C1 = 2
}

View file

@ -0,0 +1,52 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic
return block {
final core::List<core::int> #t1 = core::_GrowableList::•<core::int>(0);
core::int i;
core::int n;
{
final dynamic #0#0 = x;
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};
function ##0#7#initializer() → dynamic
return #0#0{core::List<dynamic>}.{core::List::[]}(1){(core::int) → dynamic};
late final dynamic #0#7 = ##0#7#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 = i = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final core::int #t3 = n = #0#7{core::int} in true))))
throw new _in::ReachabilityError::•();
}
for (core::int i = i, core::int n = n; i.{core::num::<}(n){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int})
#t1.{core::List::add}{Invariant}(i){(core::int) → void};
} =>#t1;
static method main() → dynamic {
self::expectEquals(self::listToString(self::test1(core::_GrowableList::_literal2<core::int>(0, 3)) as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<dynamic>), self::listToString(core::_GrowableList::_literal3<dynamic>(0, 1, 2)));
self::expectEquals(self::listToString(self::test1(core::_GrowableList::_literal2<core::int>(0, 0)) as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<dynamic>), self::listToString(core::_GrowableList::•<dynamic>(0)));
self::expectThrows(() → void => self::test1(<dynamic, dynamic>{}));
}
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 listToString(core::List<dynamic> list) → dynamic
return "[${list.{core::Iterable::map}<core::String>((dynamic e) → core::String => "${e}"){((dynamic) → core::String) → core::Iterable<core::String>}.{core::Iterable::join}(","){([core::String]) → core::String}}]";
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.";
}
}
constants {
#C1 = 2
}

View file

@ -0,0 +1,5 @@
test1(dynamic x) => [for (var [;int ;i, int n] = x; i < n; i++) i];
main() {}
expectEquals(x, y) {}
listToString(List list) => "[${list.map((e) => '${e}').join(',')}]";
expectThrows(void Function() f) {}

View file

@ -0,0 +1,48 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic
return block {
final core::List<core::int> #t1 = <core::int>[];
core::int i;
core::int n;
{
final dynamic #0#0 = x;
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
late final dynamic #0#7 = #0#0{core::List<dynamic>}.{core::List::[]}(1){(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 = i = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final dynamic #t3 = n = #0#7{core::int} in true))))
throw new _in::ReachabilityError::•();
}
for (core::int i = i, core::int n = n; i.{core::num::<}(n){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int})
#t1.{core::List::add}{Invariant}(i){(core::int) → void};
} =>#t1;
static method main() → dynamic {
self::expectEquals(self::listToString(self::test1(<core::int>[0, 3]) as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<dynamic>), self::listToString(<dynamic>[0, 1, 2]));
self::expectEquals(self::listToString(self::test1(<core::int>[0, 0]) as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<dynamic>), self::listToString(<dynamic>[]));
self::expectThrows(() → void => self::test1(<dynamic, dynamic>{}));
}
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 listToString(core::List<dynamic> list) → dynamic
return "[${list.{core::Iterable::map}<core::String>((dynamic e) → core::String => "${e}"){((dynamic) → core::String) → core::Iterable<core::String>}.{core::Iterable::join}(","){([core::String]) → core::String}}]";
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.";
}
}
constants {
#C1 = 2
}

View file

@ -0,0 +1,48 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic
return block {
final core::List<core::int> #t1 = <core::int>[];
core::int i;
core::int n;
{
final dynamic #0#0 = x;
late final dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
late final dynamic #0#7 = #0#0{core::List<dynamic>}.{core::List::[]}(1){(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 = i = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final dynamic #t3 = n = #0#7{core::int} in true))))
throw new _in::ReachabilityError::•();
}
for (core::int i = i, core::int n = n; i.{core::num::<}(n){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int})
#t1.{core::List::add}{Invariant}(i){(core::int) → void};
} =>#t1;
static method main() → dynamic {
self::expectEquals(self::listToString(self::test1(<core::int>[0, 3]) as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<dynamic>), self::listToString(<dynamic>[0, 1, 2]));
self::expectEquals(self::listToString(self::test1(<core::int>[0, 0]) as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<dynamic>), self::listToString(<dynamic>[]));
self::expectThrows(() → void => self::test1(<dynamic, dynamic>{}));
}
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 listToString(core::List<dynamic> list) → dynamic
return "[${list.{core::Iterable::map}<core::String>((dynamic e) → core::String => "${e}"){((dynamic) → core::String) → core::Iterable<core::String>}.{core::Iterable::join}(","){([core::String]) → core::String}}]";
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.";
}
}
constants {
#C1 = 2
}

View file

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

View file

@ -0,0 +1,52 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method test1(dynamic x) → dynamic
return block {
final core::List<core::int> #t1 = core::_GrowableList::•<core::int>(0);
core::int i;
core::int n;
{
final dynamic #0#0 = x;
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};
function ##0#7#initializer() → dynamic
return #0#0{core::List<dynamic>}.{core::List::[]}(1){(core::int) → dynamic};
late final dynamic #0#7 = ##0#7#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 = i = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final core::int #t3 = n = #0#7{core::int} in true))))
throw new _in::ReachabilityError::•();
}
for (core::int i = i, core::int n = n; i.{core::num::<}(n){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int})
#t1.{core::List::add}{Invariant}(i){(core::int) → void};
} =>#t1;
static method main() → dynamic {
self::expectEquals(self::listToString(self::test1(core::_GrowableList::_literal2<core::int>(0, 3)) as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<dynamic>), self::listToString(core::_GrowableList::_literal3<dynamic>(0, 1, 2)));
self::expectEquals(self::listToString(self::test1(core::_GrowableList::_literal2<core::int>(0, 0)) as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<dynamic>), self::listToString(core::_GrowableList::•<dynamic>(0)));
self::expectThrows(() → void => self::test1(<dynamic, dynamic>{}));
}
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 listToString(core::List<dynamic> list) → dynamic
return "[${list.{core::Iterable::map}<core::String>((dynamic e) → core::String => "${e}"){((dynamic) → core::String) → core::Iterable<core::String>}.{core::Iterable::join}(","){([core::String]) → core::String}}]";
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.";
}
}
constants {
#C1 = 2
}

View file

@ -167,6 +167,7 @@ patterns/pattern_matching: FormatterCrash
patterns/pattern_types: FormatterCrash
patterns/records/destructuring: FormatterCrash
patterns/records/record_pattern_inside_if_case: FormatterCrash
patterns/simple_c_style_pattern_for_in_collections: FormatterCrash
patterns/simple_if_case_in_lists: FormatterCrash
patterns/simple_if_case_map_entries: FormatterCrash
patterns/switchExpression_empty: FormatterCrash