Skip to content

Commit

Permalink
[cfe] Add inference and type checks in for-elements in lists and sets
Browse files Browse the repository at this point in the history
Change-Id: Ib8e85f674caf0565b25cf29642f4981a4a1534e5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97934
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Kevin Millikin <kmillikin@google.com>
  • Loading branch information
Dmitry Stefantsov authored and commit-bot@chromium.org committed Mar 26, 2019
1 parent 49a7fa1 commit fbc70da
Show file tree
Hide file tree
Showing 10 changed files with 3,459 additions and 20 deletions.
45 changes: 35 additions & 10 deletions pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,6 @@ class InferenceVisitor extends BodyVisitor1<void, DartType> {
: inferrer.typeSchemaEnvironment
.getStandardUpperBound(thenType, otherwiseType);
} else if (element is ForElement) {
// TODO(kmillikin): Implement inference rules for for elements.
for (VariableDeclaration declaration in element.variables) {
if (declaration.initializer != null) {
inferrer.inferExpression(declaration.initializer, declaration.type,
Expand All @@ -730,11 +729,9 @@ class InferenceVisitor extends BodyVisitor1<void, DartType> {
inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
}
inferElement(element.body, index, inferredTypeArgument, spreadTypes,
inferenceNeeded, typeChecksNeeded);
return const DynamicType();
return inferElement(element.body, index, inferredTypeArgument,
spreadTypes, inferenceNeeded, typeChecksNeeded);
} else if (element is ForInElement) {
// TODO(kmillikin): Implement inference rules for for-in elements.
if (element.variable.name == null) {
handleForInWithoutVariable(
element.variable, element.iterable, element.prologue,
Expand All @@ -749,9 +746,8 @@ class InferenceVisitor extends BodyVisitor1<void, DartType> {
inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
}
inferElement(element.body, index, inferredTypeArgument, spreadTypes,
inferenceNeeded, typeChecksNeeded);
return const DynamicType();
return inferElement(element.body, index, inferredTypeArgument,
spreadTypes, inferenceNeeded, typeChecksNeeded);
} else {
return inferrer.inferExpression(
element, inferredTypeArgument, inferenceNeeded || typeChecksNeeded,
Expand Down Expand Up @@ -816,8 +812,37 @@ class InferenceVisitor extends BodyVisitor1<void, DartType> {
item.otherwise, item, typeArgument, actualType, spreadType);
}
}
} else if (item is ForElement || item is ForInElement) {
// TODO(kmillikin): Insert type checks on loop elements.
} else if (item is ForElement) {
if (item.condition != null) {
DartType conditionType = getInferredType(item.condition, inferrer);
inferrer.ensureAssignable(inferrer.coreTypes.boolClass.rawType,
conditionType, item.condition, item.condition.fileOffset);
}
if (!inferrer.isAssignable(typeArgument, actualType)) {
parent.replaceChild(
item,
inferrer.helper.desugarSyntheticExpression(inferrer.helper
.buildProblem(
templateInvalidAssignment.withArguments(
actualType, typeArgument),
item.body.fileOffset,
1)));
} else {
checkElement(item.body, item, typeArgument, actualType, spreadType);
}
} else if (item is ForInElement) {
if (!inferrer.isAssignable(typeArgument, actualType)) {
parent.replaceChild(
item,
inferrer.helper.desugarSyntheticExpression(inferrer.helper
.buildProblem(
templateInvalidAssignment.withArguments(
actualType, typeArgument),
item.body.fileOffset,
1)));
} else {
checkElement(item.body, item, typeArgument, actualType, spreadType);
}
} else {
inferrer.ensureAssignable(typeArgument, actualType, item, item.fileOffset,
isVoidAllowed: typeArgument is VoidType);
Expand Down
97 changes: 97 additions & 0 deletions pkg/front_end/testcases/control_flow_collection_inference.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,101 @@ testIfElementErrors(Map<int, int> map) {
<int>{if (oracle("foo")) ...map else 42, null};
}

testForElement(dynamic dynVar, List<int> listInt, List<double> listDouble, int
index) {
var list10 = [for (int i = 0; oracle("foo"); i++) 42];
var set10 = {for (int i = 0; oracle("foo"); i++) 42, null};
var list11 = [for (int i = 0; oracle("foo"); i++) dynVar];
var set11 = {for (int i = 0; oracle("foo"); i++) dynVar, null};
var list12 = [for (int i = 0; oracle("foo"); i++) [42]];
var set12 = {for (int i = 0; oracle("foo"); i++) [42], null};
var list20 = [for (int i = 0; oracle("foo"); i++) ...[42]];
var set20 = {for (int i = 0; oracle("foo"); i++) ...[42], null};
var list21 = [for (int i = 0; oracle("foo"); i++) ...[dynVar]];
var set21 = {for (int i = 0; oracle("foo"); i++) ...[dynVar], null};
var list22 = [for (int i = 0; oracle("foo"); i++) ...[[42]]];
var set22 = {for (int i = 0; oracle("foo"); i++) ...[[42]], null};
var list30 = [for (int i = 0; oracle("foo"); i++) if (oracle()) ...[42]];
var set30 = {for (int i = 0; oracle("foo"); i++) if (oracle()) ...[42], null};
var list31 = [for (int i = 0; oracle("foo"); i++) if (oracle()) ...[dynVar]];
var set31 = {for (int i = 0; oracle("foo"); i++) if (oracle()) ...[dynVar], null};
var list33 = [for (int i = 0; oracle("foo"); i++) if (oracle()) ...[[42]]];
var set33 = {for (int i = 0; oracle("foo"); i++) if (oracle()) ...[[42]], null};
List<List<int>> list40 = [for (int i = 0; oracle("foo"); i++) ...[[]]];
Set<List<int>> set40 = {for (int i = 0; oracle("foo"); i++) ...[[]], null};
List<List<int>> list41 = [for (int i = 0; oracle("foo"); i++) ...{[]}];
Set<List<int>> set41 = {for (int i = 0; oracle("foo"); i++) ...{[]}, null};
List<List<int>> list42 = [for (int i = 0; oracle("foo"); i++) if (oracle()) ...[[]]];
Set<List<int>> set42 = {for (int i = 0; oracle("foo"); i++) if (oracle()) ...[[]], null};
List<int> list50 = [for (int i = 0; oracle("foo"); i++) ...[]];
Set<int> set50 = {for (int i = 0; oracle("foo"); i++) ...[], null};
List<int> list51 = [for (int i = 0; oracle("foo"); i++) ...{}];
Set<int> set51 = {for (int i = 0; oracle("foo"); i++) ...{}, null};
List<int> list52 = [for (int i = 0; oracle("foo"); i++) if (oracle()) ...[]];
Set<int> set52 = {for (int i = 0; oracle("foo"); i++) if (oracle()) ...[], null};
List<List<int>> list60 = [for (int i = 0; oracle("foo"); i++) ...[[]]];
Set<List<int>> set60 = {for (int i = 0; oracle("foo"); i++) ...[[]], null};
List<List<int>> list61 = [for (int i = 0; oracle("foo"); i++) if (oracle()) ...[[]]];
Set<List<int>> set61 = {for (int i = 0; oracle("foo"); i++) if (oracle()) ...[[]], null};
List<List<int>> list70 = [for (int i = 0; oracle("foo"); i++) []];
Set<List<int>> set70 = {for (int i = 0; oracle("foo"); i++) [], null};
List<List<int>> list71 = [for (int i = 0; oracle("foo"); i++) if (oracle()) []];
Set<List<int>> set71 = {for (int i = 0; oracle("foo"); i++) if (oracle()) [], null};
var list80 = [for (int i = 0; oracle("foo"); i++) if (oracle()) 42 else 3.14];
var set80 = {for (int i = 0; oracle("foo"); i++) if (oracle()) 42 else 3.14, null};
var list81 = [for (int i = 0; oracle("foo"); i++) if (oracle()) ...listInt else ...listDouble];
var set81 = {for (int i = 0; oracle("foo"); i++) if (oracle()) ...listInt else ...listDouble, null};
var list82 = [for (int i = 0; oracle("foo"); i++) if (oracle()) ...listInt else ...dynVar];
var set82 = {for (int i = 0; oracle("foo"); i++) if (oracle()) ...listInt else ...dynVar, null};
var list83 = [for (int i = 0; oracle("foo"); i++) if (oracle()) 42 else ...listDouble];
var set83 = {for (int i = 0; oracle("foo"); i++) if (oracle()) ...listInt else 3.14, null};
List<int> list90 = [for (int i = 0; oracle("foo"); i++) dynVar];
Set<int> set90 = {for (int i = 0; oracle("foo"); i++) dynVar, null};
List<int> list91 = [for (int i = 0; oracle("foo"); i++) ...dynVar];
Set<int> set91 = {for (int i = 0; oracle("foo"); i++) ...dynVar, null};
List<int> list100 = <int>[for (index = 0; oracle("foo"); index++) 42];
Set<int> set100 = <int>{for (index = 0; oracle("foo"); index++) 42};
var list110 = [for (var i in [1, 2, 3]) i];
var set110 = {for (var i in [1, 2, 3]) i, null};
List<int> list120 = [for (var i in dynVar) i];
Set<int> set120 = {for (var i in dynVar) i, null};
}

testForElementErrors(Map<int, int> map) async {
<int>[for (int i = 0; oracle("foo"); i++) "bar"];
<int>{for (int i = 0; oracle("foo"); i++) "bar", null};
<int>[for (int i = 0; oracle("foo"); i++) ...["bar"]];
<int>{for (int i = 0; oracle("foo"); i++) ...["bar"], null};
<int>[for (int i = 0; oracle("foo"); i++) ...map];
<int>{for (int i = 0; oracle("foo"); i++) ...map, null};
<String>[for (int i = 0; oracle("foo"); i++) if (oracle()) 42 else 3.14];
<String>{for (int i = 0; oracle("foo"); i++) if (oracle()) 42 else 3.14, null};
<int>[for (int i = 0; oracle("foo"); i++) if (oracle()) ...map else 42];
<int>{for (int i = 0; oracle("foo"); i++) if (oracle()) ...map else 42, null};
<int>[for (int i = 0; oracle("foo"); i++) if (oracle()) 42 else ...map];
<int>{for (int i = 0; oracle("foo"); i++) if (oracle()) ...map else 42, null};

final i = 0;
<int>[for (i in <int>[1]) i];
<int>{for (i in <int>[1]) i, null};

var list10 = [for (var i in "not iterable") i];
var set10 = {for (var i in "not iterable") i, null};
var list20 = [for (int i in ["not", "int"]) i];
var set20 = {for (int i in ["not", "int"]) i, null};
var list30 = [await for (var i in "not stream") i];
var set30 = {await for (var i in "not stream") i, null};
var list40 = [await for (int i in Stream.fromIterable(["not", "int"])) i];
var set40 = {await for (int i in Stream.fromIterable(["not", "int"])) i, null};
var list50 = [await for (;;) 42];
var set50 = {await for (;;) 42, null};
var list60 = [for (; "not bool";) 42];
var set60 = {for (; "not bool";) 42, null};
}

testForElementErrorsNotAsync(Stream<int> stream) {
<int>[await for (int i in stream) i];
<int>{await for (int i in stream) i};
}

main() {}
Loading

0 comments on commit fbc70da

Please sign in to comment.