[CFE] Lazy constant evaluation for maybe-evaluated expressions.
Change-Id: I21d21b33902b63a4769d6f4b342ec285c9ab46ee
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96783
Reviewed-by: Kevin Millikin <kmillikin@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index d202376..5c27dda 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -425,6 +425,8 @@
Set<TreeNode> unevaluatedNodes;
Set<Expression> replacementNodes;
+ int lazyDepth; // Current nesting depth of lazy regions.
+
bool get targetingJavaScript => numberSemantics == NumberSemantics.js;
ConstantEvaluator(this.backend, this.environmentDefines, this.typeEnvironment,
@@ -456,6 +458,7 @@
/// an error occurred during constant evaluation.
Constant evaluate(Expression node) {
evaluationRoot = node;
+ lazyDepth = 0;
try {
return _evaluateSubexpression(node);
} on _AbortDueToError catch (e) {
@@ -520,8 +523,21 @@
return expression;
}
+ /// Enter a region of lazy evaluation. All leaf nodes are evaluated normally
+ /// (to ensure inlining of referenced local variables), but composite nodes
+ /// always treat their children as unevaluated, resulting in a partially
+ /// evaluated clone of the original expression tree.
+ /// Lazy evaluation is used for the subtrees of lazy operations with
+ /// unevaluated conditions to ensure no errors are reported for problems
+ /// in the subtree as long as the subtree is potentially constant.
+ void enterLazy() => lazyDepth++;
+
+ /// Leave a (possibly nested) region of lazy evaluation.
+ void leaveLazy() => lazyDepth--;
+
/// Should this node become unevaluated because of an unevaluated child?
bool hasUnevaluatedChild(TreeNode node) {
+ if (lazyDepth != 0) return true;
return unevaluatedNodes != null && unevaluatedNodes.contains(node);
}
@@ -661,19 +677,11 @@
return report(
node, templateConstEvalNonConstantLiteral.withArguments('Map'));
}
- final Set<Constant> usedKeys = new Set<Constant>();
final List<ConstantMapEntry> entries =
new List<ConstantMapEntry>(node.entries.length);
for (int i = 0; i < node.entries.length; ++i) {
final key = _evaluateSubexpression(node.entries[i].key);
final value = _evaluateSubexpression(node.entries[i].value);
- if (!usedKeys.add(key)) {
- // TODO(kustermann): We should change the context handling from just
- // capturing the `TreeNode`s to a `(TreeNode, String message)` tuple and
- // report where the first key with the same value was.
- return report(
- node.entries[i], templateConstEvalDuplicateKey.withArguments(key));
- }
entries[i] = new ConstantMapEntry(key, value);
}
if (hasUnevaluatedChild(node)) {
@@ -687,6 +695,16 @@
new MapLiteral(mapEntries,
keyType: node.keyType, valueType: node.valueType, isConst: true));
}
+ final Set<Constant> usedKeys = new Set<Constant>();
+ for (int i = 0; i < node.entries.length; ++i) {
+ if (!usedKeys.add(entries[i].key)) {
+ // TODO(kustermann): We should change the context handling from just
+ // capturing the `TreeNode`s to a `(TreeNode, String message)` tuple and
+ // report where the first key with the same value was.
+ return report(node.entries[i],
+ templateConstEvalDuplicateKey.withArguments(entries[i].key));
+ }
+ }
final DartType keyType = evaluateDartType(node, node.keyType);
final DartType valueType = evaluateDartType(node, node.valueType);
return canonicalize(
@@ -1144,11 +1162,14 @@
visitLogicalExpression(LogicalExpression node) {
final Constant left = _evaluateSubexpression(node.left);
- if (left is UnevaluatedConstant) {
+ if (hasUnevaluatedChild(node)) {
+ enterLazy();
+ Constant right = _evaluateSubexpression(node.right);
+ leaveLazy();
return unevaluated(
node,
- new LogicalExpression(unique(left.expression), node.operator,
- cloner.clone(node.right)));
+ new LogicalExpression(unique(left.asExpression()), node.operator,
+ unique(right.asExpression())));
}
switch (node.operator) {
case '||':
@@ -1211,13 +1232,17 @@
return _evaluateSubexpression(node.then);
} else if (condition == falseConstant) {
return _evaluateSubexpression(node.otherwise);
- } else if (condition is UnevaluatedConstant) {
+ } else if (hasUnevaluatedChild(node)) {
+ enterLazy();
+ Constant then = _evaluateSubexpression(node.then);
+ Constant otherwise = _evaluateSubexpression(node.otherwise);
+ leaveLazy();
return unevaluated(
node,
new ConditionalExpression(
- unique(condition.expression),
- cloner.clone(node.then),
- cloner.clone(node.otherwise),
+ unique(condition.asExpression()),
+ unique(then.asExpression()),
+ unique(otherwise.asExpression()),
node.staticType));
} else {
return report(
@@ -1247,11 +1272,11 @@
return receiver.fieldValues[fieldRef];
}
}
- } else if (receiver is UnevaluatedConstant) {
+ } else if (hasUnevaluatedChild(node)) {
return unevaluated(
node,
- new PropertyGet(
- unique(receiver.expression), node.name, node.interfaceTarget));
+ new PropertyGet(unique(receiver.asExpression()), node.name,
+ node.interfaceTarget));
} else if (receiver is NullConstant) {
return report(node, messageConstEvalNullValue);
}
@@ -1330,7 +1355,7 @@
} else {
concatenated.add(new StringBuffer(value));
}
- } else if (constant is UnevaluatedConstant) {
+ } else if (hasUnevaluatedChild(node)) {
concatenated.add(constant);
} else {
return report(
@@ -1446,9 +1471,9 @@
visitAsExpression(AsExpression node) {
final Constant constant = _evaluateSubexpression(node.operand);
- if (constant is UnevaluatedConstant) {
+ if (hasUnevaluatedChild(node)) {
return unevaluated(
- node, new AsExpression(unique(constant.expression), node.type));
+ node, new AsExpression(unique(constant.asExpression()), node.type));
}
ensureIsSubtype(constant, evaluateDartType(node, node.type), node);
return constant;
@@ -1459,8 +1484,8 @@
if (constant is BoolConstant) {
return constant == trueConstant ? falseConstant : trueConstant;
}
- if (constant is UnevaluatedConstant) {
- return unevaluated(node, new Not(unique(constant.expression)));
+ if (hasUnevaluatedChild(node)) {
+ return unevaluated(node, new Not(unique(constant.asExpression())));
}
return report(
node,
@@ -1476,6 +1501,12 @@
visitInstantiation(Instantiation node) {
final Constant constant = _evaluateSubexpression(node.expression);
+ if (hasUnevaluatedChild(node)) {
+ return unevaluated(
+ node,
+ new Instantiation(
+ unique(constant.asExpression()), node.typeArguments));
+ }
if (constant is TearOffConstant) {
if (node.typeArguments.length ==
constant.procedure.function.typeParameters.length) {
@@ -1487,10 +1518,6 @@
'The number of type arguments supplied in the partial instantiation '
'does not match the number of type arguments of the $constant.');
}
- if (constant is UnevaluatedConstant) {
- return unevaluated(node,
- new Instantiation(unique(constant.expression), node.typeArguments));
- }
// The inner expression in an instantiation can never be null, since
// instantiations are only inferred on direct references to declarations.
throw new Exception(