[CFE] Constant evaluator doesn't throw
Change-Id: I32fb024f6c475addd430f0fd1b5b4584f0fca44b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/162507
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_collection_builders.dart b/pkg/front_end/lib/src/fasta/kernel/constant_collection_builders.dart
index e2f7865..5fd43f5 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_collection_builders.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_collection_builders.dart
@@ -18,25 +18,34 @@
L makeLiteral(List<Expression> elements);
/// Add an element to the constant list being built by this builder.
- void add(Expression element) {
+ ///
+ /// Returns [null] on success and an error-"constant" on failure, as such the
+ /// return value should be checked.
+ AbortConstant add(Expression element) {
Constant constant = evaluator._evaluateSubexpression(element);
+ if (constant is AbortConstant) return constant;
if (evaluator.shouldBeUnevaluated) {
parts.add(evaluator.unevaluated(
element, makeLiteral([evaluator.extract(constant)])));
+ return null;
} else {
- addConstant(constant, element);
+ return addConstant(constant, element);
}
}
- void addSpread(Expression spreadExpression) {
- Constant spread =
- evaluator.unlower(evaluator._evaluateSubexpression(spreadExpression));
+ /// Returns [null] on success and an error-"constant" on failure, as such the
+ /// return value should be checked.
+ AbortConstant addSpread(Expression spreadExpression) {
+ Constant constant = evaluator._evaluateSubexpression(spreadExpression);
+ if (constant is AbortConstant) return constant;
+ Constant spread = evaluator.unlower(constant);
if (evaluator.shouldBeUnevaluated) {
// Unevaluated spread
parts.add(spread);
} else if (spread == evaluator.nullConstant) {
// Null spread
- evaluator.report(spreadExpression, messageConstEvalNullValue);
+ return evaluator.createErrorConstant(
+ spreadExpression, messageConstEvalNullValue);
} else {
// Fully evaluated spread
List<Constant> entries;
@@ -46,16 +55,20 @@
entries = spread.entries;
} else {
// Not list or set in spread
- return evaluator.report(
+ return evaluator.createErrorConstant(
spreadExpression, messageConstEvalNotListOrSetInSpread);
}
for (Constant entry in entries) {
- addConstant(entry, spreadExpression);
+ AbortConstant error = addConstant(entry, spreadExpression);
+ if (error != null) return error;
}
}
+ return null;
}
- void addConstant(Constant constant, TreeNode context);
+ /// Returns [null] on success and an error-"constant" on failure, as such the
+ /// return value should be checked.
+ AbortConstant addConstant(Constant constant, TreeNode context);
Constant build();
}
@@ -70,7 +83,7 @@
new ListLiteral(elements, isConst: true);
@override
- void addConstant(Constant constant, TreeNode context) {
+ AbortConstant addConstant(Constant constant, TreeNode context) {
List<Constant> lastPart;
if (parts.last is List<Constant>) {
lastPart = parts.last;
@@ -78,7 +91,10 @@
// Probably unreachable.
parts.add(lastPart = <Constant>[]);
}
- lastPart.add(evaluator.ensureIsSubtype(constant, elementType, context));
+ Constant value = evaluator.ensureIsSubtype(constant, elementType, context);
+ if (value is AbortConstant) return value;
+ lastPart.add(value);
+ return null;
}
@override
@@ -116,16 +132,16 @@
new SetLiteral(elements, isConst: true);
@override
- void addConstant(Constant constant, TreeNode context) {
+ AbortConstant addConstant(Constant constant, TreeNode context) {
if (!evaluator.hasPrimitiveEqual(constant)) {
- evaluator.report(
+ return evaluator.createErrorConstant(
context,
templateConstEvalElementImplementsEqual.withArguments(
constant, evaluator.isNonNullableByDefault));
}
bool unseen = seen.add(constant);
if (!unseen) {
- evaluator.report(
+ return evaluator.createErrorConstant(
context,
templateConstEvalDuplicateElement.withArguments(
constant, evaluator.isNonNullableByDefault));
@@ -135,7 +151,8 @@
evaluator._weakener.visitConstant(constant) ?? constant;
bool weakUnseen = weakSeen.add(weakConstant);
if (unseen != weakUnseen) {
- evaluator.report(context, messageNonAgnosticConstant);
+ return evaluator.createErrorConstant(
+ context, messageNonAgnosticConstant);
}
}
@@ -146,7 +163,10 @@
// Probably unreachable.
parts.add(lastPart = <Constant>[]);
}
- lastPart.add(evaluator.ensureIsSubtype(constant, elementType, context));
+ Constant value = evaluator.ensureIsSubtype(constant, elementType, context);
+ if (value is AbortConstant) return value;
+ lastPart.add(value);
+ return null;
}
@override
@@ -208,45 +228,59 @@
this.original, this.keyType, this.valueType, this.evaluator);
/// Add a map entry to the constant map being built by this builder
- void add(MapEntry element) {
+ ///
+ /// Returns [null] on success and an error-"constant" on failure, as such the
+ /// return value should be checked.
+ AbortConstant add(MapEntry element) {
Constant key = evaluator._evaluateSubexpression(element.key);
+ if (key is AbortConstant) return key;
Constant value = evaluator._evaluateSubexpression(element.value);
+ if (value is AbortConstant) return value;
if (evaluator.shouldBeUnevaluated) {
parts.add(evaluator.unevaluated(
element.key,
new MapLiteral(
[new MapEntry(evaluator.extract(key), evaluator.extract(value))],
isConst: true)));
+ return null;
} else {
- addConstant(key, value, element.key, element.value);
+ return addConstant(key, value, element.key, element.value);
}
}
- void addSpread(Expression spreadExpression) {
- Constant spread =
- evaluator.unlower(evaluator._evaluateSubexpression(spreadExpression));
+ /// Returns [null] on success and an error-"constant" on failure, as such the
+ /// return value should be checked.
+ AbortConstant addSpread(Expression spreadExpression) {
+ Constant constant = evaluator._evaluateSubexpression(spreadExpression);
+ if (constant is AbortConstant) return constant;
+ Constant spread = evaluator.unlower(constant);
if (evaluator.shouldBeUnevaluated) {
// Unevaluated spread
parts.add(spread);
} else if (spread == evaluator.nullConstant) {
// Null spread
- evaluator.report(spreadExpression, messageConstEvalNullValue);
+ return evaluator.createErrorConstant(
+ spreadExpression, messageConstEvalNullValue);
} else {
// Fully evaluated spread
if (spread is MapConstant) {
for (ConstantMapEntry entry in spread.entries) {
- addConstant(
+ AbortConstant error = addConstant(
entry.key, entry.value, spreadExpression, spreadExpression);
+ if (error != null) return error;
}
} else {
// Not map in spread
- return evaluator.report(
+ return evaluator.createErrorConstant(
spreadExpression, messageConstEvalNotMapInSpread);
}
}
+ return null;
}
- void addConstant(Constant key, Constant value, TreeNode keyContext,
+ /// Returns [null] on success and an error-"constant" on failure, as such the
+ /// return value should be checked.
+ AbortConstant addConstant(Constant key, Constant value, TreeNode keyContext,
TreeNode valueContext) {
List<ConstantMapEntry> lastPart;
if (parts.last is List<ConstantMapEntry>) {
@@ -256,14 +290,14 @@
parts.add(lastPart = <ConstantMapEntry>[]);
}
if (!evaluator.hasPrimitiveEqual(key)) {
- evaluator.report(
+ return evaluator.createErrorConstant(
keyContext,
templateConstEvalKeyImplementsEqual.withArguments(
key, evaluator.isNonNullableByDefault));
}
bool unseenKey = seenKeys.add(key);
if (!unseenKey) {
- evaluator.report(
+ return evaluator.createErrorConstant(
keyContext,
templateConstEvalDuplicateKey.withArguments(
key, evaluator.isNonNullableByDefault));
@@ -272,12 +306,16 @@
Constant weakKey = evaluator._weakener.visitConstant(key) ?? key;
bool weakUnseenKey = weakSeenKeys.add(weakKey);
if (unseenKey != weakUnseenKey) {
- evaluator.report(keyContext, messageNonAgnosticConstant);
+ return evaluator.createErrorConstant(
+ keyContext, messageNonAgnosticConstant);
}
}
- lastPart.add(new ConstantMapEntry(
- evaluator.ensureIsSubtype(key, keyType, keyContext),
- evaluator.ensureIsSubtype(value, valueType, valueContext)));
+ Constant key2 = evaluator.ensureIsSubtype(key, keyType, keyContext);
+ if (key2 is AbortConstant) return key2;
+ Constant value2 = evaluator.ensureIsSubtype(value, valueType, valueContext);
+ if (value2 is AbortConstant) return value2;
+ lastPart.add(new ConstantMapEntry(key2, value2));
+ return null;
}
Constant build() {
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 7998ec1..a9808d6 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -28,6 +28,7 @@
import 'package:kernel/core_types.dart';
import 'package:kernel/kernel.dart';
import 'package:kernel/src/legacy_erasure.dart';
+import 'package:kernel/src/printer.dart' show AstPrinter, AstTextStrategy;
import 'package:kernel/type_algebra.dart';
import 'package:kernel/type_environment.dart';
import 'package:kernel/target/targets.dart';
@@ -885,59 +886,66 @@
_staticTypeContext = context;
seenUnevaluatedChild = false;
lazyDepth = 0;
- try {
- Constant result = _evaluateSubexpression(node);
- if (result is UnevaluatedConstant) {
- if (errorOnUnevaluatedConstant) {
- return report(node, messageConstEvalUnevaluated);
+ Constant result = _evaluateSubexpression(node);
+ if (result is AbortConstant) {
+ if (result is _AbortDueToErrorConstant) {
+ final Uri uri = getFileUri(result.node);
+ final int fileOffset = getFileOffset(uri, result.node);
+ final LocatedMessage locatedMessageActualError =
+ result.message.withLocation(uri, fileOffset, noLength);
+
+ final List<LocatedMessage> contextMessages = <LocatedMessage>[
+ locatedMessageActualError
+ ];
+ if (result.context != null) contextMessages.addAll(result.context);
+ for (final TreeNode node in contextChain) {
+ if (node == result.node) continue;
+ final Uri uri = getFileUri(node);
+ final int fileOffset = getFileOffset(uri, node);
+ contextMessages.add(
+ messageConstEvalContext.withLocation(uri, fileOffset, noLength));
+ }
+
+ {
+ final Uri uri = getFileUri(node);
+ final int fileOffset = getFileOffset(uri, node);
+ final LocatedMessage locatedMessage = messageConstEvalStartingPoint
+ .withLocation(uri, fileOffset, noLength);
+ errorReporter.report(locatedMessage, contextMessages);
}
return new UnevaluatedConstant(
- removeRedundantFileUriExpressions(result.expression));
+ new InvalidExpression(result.message.message));
}
- return result;
- } on _AbortDueToError catch (e) {
- final Uri uri = getFileUri(e.node);
- final int fileOffset = getFileOffset(uri, e.node);
- final LocatedMessage locatedMessageActualError =
- e.message.withLocation(uri, fileOffset, noLength);
-
- final List<LocatedMessage> contextMessages = <LocatedMessage>[
- locatedMessageActualError
- ];
- if (e.context != null) contextMessages.addAll(e.context);
- for (final TreeNode node in contextChain) {
- if (node == e.node) continue;
- final Uri uri = getFileUri(node);
- final int fileOffset = getFileOffset(uri, node);
- contextMessages.add(
- messageConstEvalContext.withLocation(uri, fileOffset, noLength));
+ if (result is _AbortDueToInvalidExpressionConstant) {
+ InvalidExpression invalid = new InvalidExpression(result.message)
+ ..fileOffset = node.fileOffset;
+ errorReporter.reportInvalidExpression(invalid);
+ return new UnevaluatedConstant(invalid);
}
-
- {
- final Uri uri = getFileUri(node);
- final int fileOffset = getFileOffset(uri, node);
- final LocatedMessage locatedMessage = messageConstEvalStartingPoint
- .withLocation(uri, fileOffset, noLength);
- errorReporter.report(locatedMessage, contextMessages);
- }
- return new UnevaluatedConstant(new InvalidExpression(e.message.message));
- } on _AbortDueToInvalidExpression catch (e) {
- InvalidExpression invalid = new InvalidExpression(e.message)
- ..fileOffset = node.fileOffset;
- errorReporter.reportInvalidExpression(invalid);
- return new UnevaluatedConstant(invalid);
+ throw "Unexpected error constant";
}
+ if (result is UnevaluatedConstant) {
+ if (errorOnUnevaluatedConstant) {
+ return createErrorConstant(node, messageConstEvalUnevaluated);
+ }
+ return new UnevaluatedConstant(
+ removeRedundantFileUriExpressions(result.expression));
+ }
+ return result;
}
- /// Report an error that has been detected during constant evaluation.
- Null report(TreeNode node, Message message, {List<LocatedMessage> context}) {
- throw new _AbortDueToError(node, message, context: context);
+ /// Create an error-constant indicating that an error has been detected during
+ /// constant evaluation.
+ AbortConstant createErrorConstant(TreeNode node, Message message,
+ {List<LocatedMessage> context}) {
+ return new _AbortDueToErrorConstant(node, message, context: context);
}
- /// Report a construct that should not occur inside a potentially constant
- /// expression. It is assumed that an error has already been reported.
- Null reportInvalid(TreeNode node, String message) {
- throw new _AbortDueToInvalidExpression(node, message);
+ /// Create an error-constant indicating a construct that should not occur
+ /// inside a potentially constant expression.
+ /// It is assumed that an error has already been reported.
+ AbortConstant createInvalidExpressionConstant(TreeNode node, String message) {
+ return new _AbortDueToInvalidExpressionConstant(node, message);
}
/// Produce an unevaluated constant node for an expression.
@@ -1007,8 +1015,12 @@
}
/// Evaluate [node] and possibly cache the evaluation result.
- /// @throws _AbortDueToError or _AbortDueToInvalidExpression if expression
- /// can't be evaluated.
+ ///
+ /// Returns [_AbortDueToErrorConstant] or
+ /// [_AbortDueToInvalidExpressionConstant] (both of which is an
+ /// [AbortConstant]) if the expression can't be evaluated.
+ /// As such the return value should be checked (e.g. `is AbortConstant`)
+ /// before further use.
Constant _evaluateSubexpression(Expression node) {
bool wasUnevaluated = seenUnevaluatedChild;
seenUnevaluatedChild = false;
@@ -1017,18 +1029,27 @@
// We only try to evaluate the same [node] *once* within an empty
// environment.
if (nodeCache.containsKey(node)) {
- result = nodeCache[node] ?? report(node, messageConstEvalCircularity);
+ result = nodeCache[node];
+ if (result == null) {
+ // [null] is a sentinel value only used when still evaluating the same
+ // node.
+ return createErrorConstant(node, messageConstEvalCircularity);
+ }
} else {
nodeCache[node] = null;
- try {
- result = nodeCache[node] = node.accept(this);
- } catch (e) {
+ result = node.accept(this);
+ if (result is AbortConstant) {
nodeCache.remove(node);
- rethrow;
+ return result;
+ } else {
+ nodeCache[node] = result;
}
}
} else {
result = node.accept(this);
+ if (result is AbortConstant) {
+ return result;
+ }
}
seenUnevaluatedChild = wasUnevaluated || result is UnevaluatedConstant;
return result;
@@ -1069,7 +1090,7 @@
Constant defaultTreeNode(Node node) {
// Only a subset of the expression language is valid for constant
// evaluation.
- return reportInvalid(
+ return createInvalidExpressionConstant(
node, 'Constant evaluation has no support for ${node.runtimeType}!');
}
@@ -1105,7 +1126,14 @@
@override
Constant visitTypeLiteral(TypeLiteral node) {
- final DartType type = evaluateDartType(node, node.type);
+ final DartType type = _evaluateDartType(node, node.type);
+ if (type == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(type != null);
return canonicalize(new TypeLiteralConstant(type));
}
@@ -1117,6 +1145,7 @@
result = runInsideContext(constant.expression, () {
return _evaluateSubexpression(constant.expression);
});
+ if (result is AbortConstant) return result;
}
// If there were already constants in the AST then we make sure we
// re-canonicalize them. After running the transformer we will therefore
@@ -1128,12 +1157,13 @@
@override
Constant visitListLiteral(ListLiteral node) {
if (!node.isConst) {
- return reportInvalid(node, "Non-constant list literal");
+ return createInvalidExpressionConstant(node, "Non-constant list literal");
}
final ListConstantBuilder builder =
new ListConstantBuilder(node, convertType(node.typeArgument), this);
for (Expression element in node.expressions) {
- builder.add(element);
+ AbortConstant error = builder.add(element);
+ if (error != null) return error;
}
return builder.build();
}
@@ -1143,7 +1173,8 @@
final ListConstantBuilder builder =
new ListConstantBuilder(node, convertType(node.typeArgument), this);
for (Expression list in node.lists) {
- builder.addSpread(list);
+ AbortConstant error = builder.addSpread(list);
+ if (error != null) return error;
}
return builder.build();
}
@@ -1151,12 +1182,13 @@
@override
Constant visitSetLiteral(SetLiteral node) {
if (!node.isConst) {
- return reportInvalid(node, "Non-constant set literal");
+ return createInvalidExpressionConstant(node, "Non-constant set literal");
}
final SetConstantBuilder builder =
new SetConstantBuilder(node, convertType(node.typeArgument), this);
for (Expression element in node.expressions) {
- builder.add(element);
+ AbortConstant error = builder.add(element);
+ if (error != null) return error;
}
return builder.build();
}
@@ -1166,7 +1198,8 @@
final SetConstantBuilder builder =
new SetConstantBuilder(node, convertType(node.typeArgument), this);
for (Expression set_ in node.sets) {
- builder.addSpread(set_);
+ AbortConstant error = builder.addSpread(set_);
+ if (error != null) return error;
}
return builder.build();
}
@@ -1174,12 +1207,13 @@
@override
Constant visitMapLiteral(MapLiteral node) {
if (!node.isConst) {
- return reportInvalid(node, "Non-constant map literal");
+ return createInvalidExpressionConstant(node, "Non-constant map literal");
}
final MapConstantBuilder builder = new MapConstantBuilder(
node, convertType(node.keyType), convertType(node.valueType), this);
for (MapEntry element in node.entries) {
- builder.add(element);
+ AbortConstant error = builder.add(element);
+ if (error != null) return error;
}
return builder.build();
}
@@ -1189,36 +1223,53 @@
final MapConstantBuilder builder = new MapConstantBuilder(
node, convertType(node.keyType), convertType(node.valueType), this);
for (Expression map in node.maps) {
- builder.addSpread(map);
+ AbortConstant error = builder.addSpread(map);
+ if (error != null) return error;
}
return builder.build();
}
@override
Constant visitFunctionExpression(FunctionExpression node) {
- return reportInvalid(node, "Function literal");
+ return createInvalidExpressionConstant(node, "Function literal");
}
@override
Constant visitConstructorInvocation(ConstructorInvocation node) {
if (!node.isConst) {
- return reportInvalid(
+ return createInvalidExpressionConstant(
node, 'Non-constant constructor invocation "$node".');
}
final Constructor constructor = node.target;
- checkConstructorConst(node, constructor);
+ AbortConstant error = checkConstructorConst(node, constructor);
+ if (error != null) return error;
final Class klass = constructor.enclosingClass;
if (klass.isAbstract) {
// Probably unreachable.
- return reportInvalid(
+ return createInvalidExpressionConstant(
node, 'Constructor "$node" belongs to abstract class "${klass}".');
}
final List<Constant> positionals =
- evaluatePositionalArguments(node.arguments);
- final Map<String, Constant> named = evaluateNamedArguments(node.arguments);
+ _evaluatePositionalArguments(node.arguments);
+ if (positionals == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(positionals != null);
+
+ final Map<String, Constant> named = _evaluateNamedArguments(node.arguments);
+ if (named == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(named != null);
bool isSymbol = klass == coreTypes.internalSymbolClass;
if (isSymbol && shouldBeUnevaluated) {
@@ -1237,14 +1288,22 @@
if (nameValue is StringConstant && isValidSymbolName(nameValue.value)) {
return canonicalize(new SymbolConstant(nameValue.value, null));
}
- return report(
+ return createErrorConstant(
node.arguments.positional.first,
templateConstEvalInvalidSymbolName.withArguments(
nameValue, isNonNullableByDefault));
}
- final List<DartType> typeArguments =
- convertTypes(evaluateTypeArguments(node, node.arguments));
+ List<DartType> types = _evaluateTypeArguments(node, node.arguments);
+ if (types == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(types != null);
+
+ final List<DartType> typeArguments = convertTypes(types);
// Fill in any missing type arguments with "dynamic".
for (int i = typeArguments.length; i < klass.typeParameters.length; i++) {
@@ -1259,13 +1318,15 @@
// initialize the fields of the new instance.
if (shouldBeUnevaluated) {
enterLazy();
- handleConstructorInvocation(
+ AbortConstant error = handleConstructorInvocation(
constructor, typeArguments, positionals, named);
+ if (error != null) return error;
leaveLazy();
return unevaluated(node, instanceBuilder.buildUnevaluatedInstance());
}
- handleConstructorInvocation(
+ AbortConstant error = handleConstructorInvocation(
constructor, typeArguments, positionals, named);
+ if (error != null) return error;
if (shouldBeUnevaluated) {
return unevaluated(node, instanceBuilder.buildUnevaluatedInstance());
}
@@ -1274,18 +1335,22 @@
});
}
- void checkConstructorConst(TreeNode node, Constructor constructor) {
+ /// Returns [null] on success and an error-"constant" on failure, as such the
+ /// return value should be checked.
+ AbortConstant checkConstructorConst(TreeNode node, Constructor constructor) {
if (!constructor.isConst) {
- reportInvalid(node, 'Non-const constructor invocation.');
+ return createInvalidExpressionConstant(
+ node, 'Non-const constructor invocation.');
}
if (constructor.function.body != null &&
constructor.function.body is! EmptyStatement) {
// Probably unreachable.
- reportInvalid(
+ return createInvalidExpressionConstant(
node,
'Constructor "$node" has non-trivial body '
'"${constructor.function.body.runtimeType}".');
}
+ return null;
}
@override
@@ -1293,18 +1358,32 @@
return withNewInstanceBuilder(
node.classNode, convertTypes(node.typeArguments), () {
for (AssertStatement statement in node.asserts) {
- checkAssert(statement);
+ AbortConstant error = checkAssert(statement);
+ if (error != null) return error;
}
+ AbortConstant error;
node.fieldValues.forEach((Reference fieldRef, Expression value) {
- instanceBuilder.setFieldValue(
- fieldRef.asField, _evaluateSubexpression(value));
- });
- node.unusedArguments.forEach((Expression value) {
+ if (error != null) return;
Constant constant = _evaluateSubexpression(value);
+ if (constant is AbortConstant) {
+ error ??= constant;
+ return;
+ }
+ instanceBuilder.setFieldValue(fieldRef.asField, constant);
+ });
+ if (error != null) return error;
+ node.unusedArguments.forEach((Expression value) {
+ if (error != null) return;
+ Constant constant = _evaluateSubexpression(value);
+ if (constant is AbortConstant) {
+ error ??= constant;
+ return;
+ }
if (constant is UnevaluatedConstant) {
instanceBuilder.unusedArguments.add(extract(constant));
}
});
+ if (error != null) return error;
if (shouldBeUnevaluated) {
return unevaluated(node, instanceBuilder.buildUnevaluatedInstance());
}
@@ -1438,7 +1517,9 @@
!nonUsableKeywords.contains(name);
}
- void handleConstructorInvocation(
+ /// Returns [null] on success and an error-"constant" on failure, as such the
+ /// return value should be checked.
+ AbortConstant handleConstructorInvocation(
Constructor constructor,
List<DartType> typeArguments,
List<Constant> positionalArguments,
@@ -1462,12 +1543,14 @@
? positionalArguments[i]
// TODO(johnniwinther): This should call [_evaluateSubexpression].
: _evaluateNullableSubexpression(parameter.initializer);
+ if (value is AbortConstant) return value;
env.addVariableValue(parameter, value);
}
for (final VariableDeclaration parameter in function.namedParameters) {
final Constant value = namedArguments[parameter.name] ??
// TODO(johnniwinther): This should call [_evaluateSubexpression].
_evaluateNullableSubexpression(parameter.initializer);
+ if (value is AbortConstant) return value;
env.addVariableValue(parameter, value);
}
@@ -1475,37 +1558,87 @@
// setup.
for (final Field field in klass.fields) {
if (!field.isStatic) {
- instanceBuilder.setFieldValue(
- field, _evaluateNullableSubexpression(field.initializer));
+ Constant constant =
+ _evaluateNullableSubexpression(field.initializer);
+ if (constant is AbortConstant) return constant;
+ instanceBuilder.setFieldValue(field, constant);
}
}
for (final Initializer init in constructor.initializers) {
if (init is FieldInitializer) {
- instanceBuilder.setFieldValue(
- init.field, _evaluateSubexpression(init.value));
+ Constant constant = _evaluateSubexpression(init.value);
+ if (constant is AbortConstant) return constant;
+ instanceBuilder.setFieldValue(init.field, constant);
} else if (init is LocalInitializer) {
final VariableDeclaration variable = init.variable;
- env.addVariableValue(
- variable, _evaluateSubexpression(variable.initializer));
+ Constant constant = _evaluateSubexpression(variable.initializer);
+ if (constant is AbortConstant) return constant;
+ env.addVariableValue(variable, constant);
} else if (init is SuperInitializer) {
- checkConstructorConst(init, constructor);
- handleConstructorInvocation(
- init.target,
- evaluateSuperTypeArguments(
- init, constructor.enclosingClass.supertype),
- evaluatePositionalArguments(init.arguments),
- evaluateNamedArguments(init.arguments));
+ AbortConstant error = checkConstructorConst(init, constructor);
+ if (error != null) return error;
+ List<DartType> types = _evaluateSuperTypeArguments(
+ init, constructor.enclosingClass.supertype);
+ if (types == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(types != null);
+
+ List<Constant> positionalArguments =
+ _evaluatePositionalArguments(init.arguments);
+ if (positionalArguments == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(positionalArguments != null);
+ Map<String, Constant> namedArguments =
+ _evaluateNamedArguments(init.arguments);
+ if (namedArguments == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(namedArguments != null);
+ error = handleConstructorInvocation(
+ init.target, types, positionalArguments, namedArguments);
+ if (error != null) return error;
} else if (init is RedirectingInitializer) {
// Since a redirecting constructor targets a constructor of the same
// class, we pass the same [typeArguments].
- checkConstructorConst(init, constructor);
- handleConstructorInvocation(
- init.target,
- typeArguments,
- evaluatePositionalArguments(init.arguments),
- evaluateNamedArguments(init.arguments));
+ AbortConstant error = checkConstructorConst(init, constructor);
+ if (error != null) return error;
+ List<Constant> positionalArguments =
+ _evaluatePositionalArguments(init.arguments);
+ if (positionalArguments == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(positionalArguments != null);
+
+ Map<String, Constant> namedArguments =
+ _evaluateNamedArguments(init.arguments);
+ if (namedArguments == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(namedArguments != null);
+
+ error = handleConstructorInvocation(init.target, typeArguments,
+ positionalArguments, namedArguments);
+ if (error != null) return error;
} else if (init is AssertInitializer) {
- checkAssert(init.statement);
+ AbortConstant error = checkAssert(init.statement);
+ if (error != null) return error;
} else {
// InvalidInitializer or new Initializers.
// Probably unreachable. InvalidInitializer is (currently) only
@@ -1513,7 +1646,7 @@
// super that takes no arguments. It thus cannot be const.
// Explicit constructors with incorrect super calls will get a
// ShadowInvalidInitializer which is actually a LocalInitializer.
- return reportInvalid(
+ return createInvalidExpressionConstant(
constructor,
'No support for handling initializer of type '
'"${init.runtimeType}".');
@@ -1523,18 +1656,24 @@
for (UnevaluatedConstant constant in env.unevaluatedUnreadConstants) {
instanceBuilder.unusedArguments.add(extract(constant));
}
+ return null;
});
});
}
- void checkAssert(AssertStatement statement) {
+ /// Returns [null] on success and an error-"constant" on failure, as such the
+ /// return value should be checked.
+ AbortConstant checkAssert(AssertStatement statement) {
final Constant condition = _evaluateSubexpression(statement.condition);
+ if (condition is AbortConstant) return condition;
if (shouldBeUnevaluated) {
Expression message = null;
if (statement.message != null) {
enterLazy();
- message = extract(_evaluateSubexpression(statement.message));
+ Constant constant = _evaluateSubexpression(statement.message);
+ if (constant is AbortConstant) return constant;
+ message = extract(constant);
leaveLazy();
}
instanceBuilder.asserts.add(new AssertStatement(extract(condition),
@@ -1544,21 +1683,23 @@
} else if (condition is BoolConstant) {
if (!condition.value) {
if (statement.message == null) {
- report(statement.condition, messageConstEvalFailedAssertion);
+ return createErrorConstant(
+ statement.condition, messageConstEvalFailedAssertion);
}
final Constant message = _evaluateSubexpression(statement.message);
+ if (message is AbortConstant) return message;
if (shouldBeUnevaluated) {
instanceBuilder.asserts.add(new AssertStatement(extract(condition),
message: extract(message),
conditionStartOffset: statement.conditionStartOffset,
conditionEndOffset: statement.conditionEndOffset));
} else if (message is StringConstant) {
- report(
+ return createErrorConstant(
statement.condition,
templateConstEvalFailedAssertionWithMessage
.withArguments(message.value));
} else {
- report(
+ return createErrorConstant(
statement.message,
templateConstEvalInvalidType.withArguments(
message,
@@ -1568,7 +1709,7 @@
}
}
} else {
- report(
+ return createErrorConstant(
statement.condition,
templateConstEvalInvalidType.withArguments(
condition,
@@ -1576,23 +1717,34 @@
condition.getType(_staticTypeContext),
isNonNullableByDefault));
}
+
+ return null;
}
@override
Constant visitInvalidExpression(InvalidExpression node) {
- return reportInvalid(node, node.message);
+ return createInvalidExpressionConstant(node, node.message);
}
@override
Constant visitMethodInvocation(MethodInvocation node) {
// We have no support for generic method invocation atm.
if (node.arguments.named.isNotEmpty) {
- return reportInvalid(node, "generic method invocation");
+ return createInvalidExpressionConstant(node, "generic method invocation");
}
final Constant receiver = _evaluateSubexpression(node.receiver);
+ if (receiver is AbortConstant) return receiver;
final List<Constant> arguments =
- evaluatePositionalArguments(node.arguments);
+ _evaluatePositionalArguments(node.arguments);
+
+ if (arguments == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(arguments != null);
if (shouldBeUnevaluated) {
return unevaluated(
@@ -1619,7 +1771,7 @@
return doubleSpecialCases(receiver, right) ??
makeBoolConstant(receiver == right);
} else {
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidEqualsOperandType.withArguments(receiver,
receiver.getType(_staticTypeContext), isNonNullableByDefault));
@@ -1636,7 +1788,7 @@
return canonicalize(
new StringConstant(receiver.value + other.value));
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidBinaryOperandType.withArguments(
'+',
@@ -1657,7 +1809,7 @@
} else if (other is DoubleConstant) {
if ((op == '|' || op == '&' || op == '^') ||
(op == '<<' || op == '>>' || op == '>>>')) {
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidBinaryOperandType.withArguments(
op,
@@ -1670,7 +1822,7 @@
return canonicalize(evaluateBinaryNumericOperation(
op, receiverValue, other.value, node));
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidBinaryOperandType.withArguments(
op,
@@ -1682,7 +1834,7 @@
} else if (receiver is DoubleConstant) {
if ((op == '|' || op == '&' || op == '^') ||
(op == '<<' || op == '>>' || op == '>>>')) {
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidBinaryOperandType.withArguments(
op,
@@ -1704,7 +1856,7 @@
return canonicalize(
evaluateBinaryNumericOperation(op, receiver.value, value, node));
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidBinaryOperandType.withArguments(
op,
@@ -1731,10 +1883,10 @@
}
}
} else if (receiver is NullConstant) {
- return report(node, messageConstEvalNullValue);
+ return createErrorConstant(node, messageConstEvalNullValue);
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidMethodInvocation.withArguments(
op, receiver, isNonNullableByDefault));
@@ -1743,9 +1895,11 @@
@override
Constant visitLogicalExpression(LogicalExpression node) {
final Constant left = _evaluateSubexpression(node.left);
+ if (left is AbortConstant) return left;
if (shouldBeUnevaluated) {
enterLazy();
Constant right = _evaluateSubexpression(node.right);
+ if (right is AbortConstant) return right;
leaveLazy();
return unevaluated(node,
new LogicalExpression(extract(left), node.operator, extract(right)));
@@ -1756,11 +1910,12 @@
if (left.value) return trueConstant;
final Constant right = _evaluateSubexpression(node.right);
+ if (right is AbortConstant) return right;
if (right is BoolConstant || right is UnevaluatedConstant) {
return right;
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidBinaryOperandType.withArguments(
node.operator,
@@ -1769,7 +1924,7 @@
right.getType(_staticTypeContext),
isNonNullableByDefault));
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidMethodInvocation.withArguments(
node.operator, left, isNonNullableByDefault));
@@ -1778,11 +1933,12 @@
if (!left.value) return falseConstant;
final Constant right = _evaluateSubexpression(node.right);
+ if (right is AbortConstant) return right;
if (right is BoolConstant || right is UnevaluatedConstant) {
return right;
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidBinaryOperandType.withArguments(
node.operator,
@@ -1791,7 +1947,7 @@
right.getType(_staticTypeContext),
isNonNullableByDefault));
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidMethodInvocation.withArguments(
node.operator, left, isNonNullableByDefault));
@@ -1802,7 +1958,7 @@
: _evaluateSubexpression(node.right);
default:
// Probably unreachable.
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidMethodInvocation.withArguments(
node.operator, left, isNonNullableByDefault));
@@ -1812,6 +1968,7 @@
@override
Constant visitConditionalExpression(ConditionalExpression node) {
final Constant condition = _evaluateSubexpression(node.condition);
+ if (condition is AbortConstant) return condition;
if (condition == trueConstant) {
return _evaluateSubexpression(node.then);
} else if (condition == falseConstant) {
@@ -1819,14 +1976,16 @@
} else if (shouldBeUnevaluated) {
enterLazy();
Constant then = _evaluateSubexpression(node.then);
+ if (then is AbortConstant) return then;
Constant otherwise = _evaluateSubexpression(node.otherwise);
+ if (otherwise is AbortConstant) return otherwise;
leaveLazy();
return unevaluated(
node,
new ConditionalExpression(extract(condition), extract(then),
extract(otherwise), node.staticType));
} else {
- return report(
+ return createErrorConstant(
node.condition,
templateConstEvalInvalidType.withArguments(
condition,
@@ -1843,7 +2002,7 @@
// const.
// Access "this" during instance creation.
if (instanceBuilder == null) {
- return report(node, messageNotAConstantExpression);
+ return createErrorConstant(node, messageNotAConstantExpression);
}
for (final Field field in instanceBuilder.fields.keys) {
@@ -1855,20 +2014,21 @@
// Meant as a "stable backstop for situations where Fasta fails to
// rewrite various erroneous constructs into invalid expressions".
// Probably unreachable.
- return reportInvalid(node,
+ return createInvalidExpressionConstant(node,
'Could not evaluate field get ${node.name} on incomplete instance');
}
final Constant receiver = _evaluateSubexpression(node.receiver);
+ if (receiver is AbortConstant) return receiver;
if (receiver is StringConstant && node.name.text == 'length') {
return canonicalize(intFolder.makeIntConstant(receiver.value.length));
} else if (shouldBeUnevaluated) {
return unevaluated(node,
new PropertyGet(extract(receiver), node.name, node.interfaceTarget));
} else if (receiver is NullConstant) {
- return report(node, messageConstEvalNullValue);
+ return createErrorConstant(node, messageConstEvalNullValue);
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidPropertyGet.withArguments(
node.name.text, receiver, isNonNullableByDefault));
@@ -1876,8 +2036,9 @@
@override
Constant visitLet(Let node) {
- env.addVariableValue(
- node.variable, _evaluateSubexpression(node.variable.initializer));
+ Constant value = _evaluateSubexpression(node.variable.initializer);
+ if (value is AbortConstant) return value;
+ env.addVariableValue(node.variable, value);
return _evaluateSubexpression(node.body);
}
@@ -1892,7 +2053,7 @@
final VariableDeclaration variable = node.variable;
if (variable.parent is Let || _isFormalParameter(variable)) {
return env.lookupVariable(node.variable) ??
- report(
+ createErrorConstant(
node,
templateConstEvalNonConstantVariableGet
.withArguments(variable.name));
@@ -1900,7 +2061,8 @@
if (variable.isConst) {
return _evaluateSubexpression(variable.initializer);
}
- return reportInvalid(node, 'Variable get of a non-const variable.');
+ return createInvalidExpressionConstant(
+ node, 'Variable get of a non-const variable.');
}
/// Computes the constant for [expression] defined in the context of [member].
@@ -1913,6 +2075,7 @@
_staticTypeContext = new StaticTypeContext(member, typeEnvironment);
Constant constant = runInsideContext(member, () {
Constant constant = _evaluateSubexpression(expression);
+ if (constant is AbortConstant) return constant;
if (_staticTypeContext.nonNullableByDefaultCompiledMode ==
NonNullableByDefaultCompiledMode.Agnostic &&
evaluationMode == EvaluationMode.weak) {
@@ -1932,7 +2095,7 @@
if (target.isConst) {
return _evaluateExpressionInContext(target, target.initializer);
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidStaticInvocation
.withArguments(target.name.text));
@@ -1940,14 +2103,13 @@
if (target.kind == ProcedureKind.Method) {
return canonicalize(new TearOffConstant(target));
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidStaticInvocation
.withArguments(target.name.text));
} else {
- reportInvalid(
+ return createInvalidExpressionConstant(
node, 'No support for ${target.runtimeType} in a static-get.');
- return null;
}
});
}
@@ -1957,6 +2119,7 @@
final List<Object> concatenated = <Object>[new StringBuffer()];
for (int i = 0; i < node.expressions.length; i++) {
Constant constant = _evaluateSubexpression(node.expressions[i]);
+ if (constant is AbortConstant) return constant;
if (constant is PrimitiveConstant<Object>) {
String value;
if (constant is DoubleConstant && intFolder.isInt(constant)) {
@@ -1976,7 +2139,7 @@
// error reporting till later.
concatenated.add(constant);
} else {
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidStringInterpolationOperand.withArguments(
constant, isNonNullableByDefault));
@@ -2082,8 +2245,24 @@
Constant visitStaticInvocation(StaticInvocation node) {
final Procedure target = node.target;
final Arguments arguments = node.arguments;
- final List<Constant> positionals = evaluatePositionalArguments(arguments);
- final Map<String, Constant> named = evaluateNamedArguments(arguments);
+ final List<Constant> positionals = _evaluatePositionalArguments(arguments);
+ if (positionals == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(positionals != null);
+
+ final Map<String, Constant> named = _evaluateNamedArguments(arguments);
+ if (named == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(named != null);
+
if (shouldBeUnevaluated) {
return unevaluated(
node,
@@ -2107,7 +2286,7 @@
return _handleHasEnvironment(name);
}
} else if (name is NullConstant) {
- return report(node, messageConstEvalNullValue);
+ return createErrorConstant(node, messageConstEvalNullValue);
}
} else {
// Leave environment constant unevaluated.
@@ -2136,7 +2315,7 @@
Constant weakResult = makeBoolConstant(
identical(weakLeft ?? left, weakRight ?? right));
if (!identical(result, weakResult)) {
- report(node, messageNonAgnosticConstant);
+ return createErrorConstant(node, messageNonAgnosticConstant);
}
}
}
@@ -2151,7 +2330,7 @@
return evaluateIdentical();
}
} else if (target.isExtensionMember) {
- return report(node, messageConstEvalExtension);
+ return createErrorConstant(node, messageConstEvalExtension);
}
String name = target.name.text;
@@ -2162,12 +2341,13 @@
name = '${target.enclosingClass.name}.${name}';
}
}
- return reportInvalid(node, "Invocation of $name");
+ return createInvalidExpressionConstant(node, "Invocation of $name");
}
@override
Constant visitAsExpression(AsExpression node) {
final Constant constant = _evaluateSubexpression(node.operand);
+ if (constant is AbortConstant) return constant;
if (shouldBeUnevaluated) {
return unevaluated(
node,
@@ -2175,12 +2355,23 @@
..isForNonNullableByDefault =
_staticTypeContext.isNonNullableByDefault);
}
- return ensureIsSubtype(constant, evaluateDartType(node, node.type), node);
+ DartType type = _evaluateDartType(node, node.type);
+ if (type == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(type != null);
+ return ensureIsSubtype(constant, type, node);
}
@override
Constant visitIsExpression(IsExpression node) {
+ // TODO(jensj): Why does this call .accept directly?
+ // (@askesc says it's probably an oversight)
final Constant constant = node.operand.accept(this);
+ if (constant is AbortConstant) return constant;
if (shouldBeUnevaluated) {
return unevaluated(
node,
@@ -2189,7 +2380,14 @@
..flags = node.flags);
}
- DartType type = evaluateDartType(node, node.type);
+ DartType type = _evaluateDartType(node, node.type);
+ if (type == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(type != null);
bool performIs(Constant constant, {bool strongMode}) {
assert(strongMode != null);
@@ -2245,7 +2443,7 @@
Constant weakConstant = _weakener.visitConstant(constant) ?? constant;
bool weakResult = performIs(weakConstant, strongMode: false);
if (strongResult != weakResult) {
- return report(node, messageNonAgnosticConstant);
+ return createErrorConstant(node, messageNonAgnosticConstant);
}
return makeBoolConstant(strongResult);
case EvaluationMode.weak:
@@ -2257,13 +2455,14 @@
@override
Constant visitNot(Not node) {
final Constant constant = _evaluateSubexpression(node.operand);
+ if (constant is AbortConstant) return constant;
if (constant is BoolConstant) {
return makeBoolConstant(constant != trueConstant);
}
if (shouldBeUnevaluated) {
return unevaluated(node, new Not(extract(constant)));
}
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidType.withArguments(
constant,
@@ -2275,8 +2474,9 @@
@override
Constant visitNullCheck(NullCheck node) {
final Constant constant = _evaluateSubexpression(node.operand);
+ if (constant is AbortConstant) return constant;
if (constant is NullConstant) {
- return report(node, messageConstEvalNonNull);
+ return createErrorConstant(node, messageConstEvalNonNull);
}
if (shouldBeUnevaluated) {
return unevaluated(node, new NullCheck(extract(constant)));
@@ -2294,6 +2494,7 @@
@override
Constant visitInstantiation(Instantiation node) {
final Constant constant = _evaluateSubexpression(node.expression);
+ if (constant is AbortConstant) return constant;
if (shouldBeUnevaluated) {
return unevaluated(
node,
@@ -2303,13 +2504,21 @@
if (constant is TearOffConstant) {
if (node.typeArguments.length ==
constant.procedure.function.typeParameters.length) {
- final List<DartType> typeArguments =
- convertTypes(evaluateDartTypes(node, node.typeArguments));
+ List<DartType> types = _evaluateDartTypes(node, node.typeArguments);
+ if (types == null && _gotError != null) {
+ AbortConstant error = _gotError;
+ _gotError = null;
+ return error;
+ }
+ assert(_gotError == null);
+ assert(types != null);
+
+ final List<DartType> typeArguments = convertTypes(types);
return canonicalize(
new PartialInstantiationConstant(constant, typeArguments));
}
// Probably unreachable.
- return reportInvalid(
+ return createInvalidExpressionConstant(
node,
'The number of type arguments supplied in the partial instantiation '
'does not match the number of type arguments of the $constant.');
@@ -2317,13 +2526,13 @@
// The inner expression in an instantiation can never be null, since
// instantiations are only inferred on direct references to declarations.
// Probably unreachable.
- return reportInvalid(
+ return createInvalidExpressionConstant(
node, 'Only tear-off constants can be partially instantiated.');
}
@override
Constant visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
- return report(
+ return createErrorConstant(
node, templateConstEvalDeferredLibrary.withArguments(node.import.name));
}
@@ -2398,6 +2607,8 @@
return result;
}
+ /// Note that this returns an error-constant on error and as such the
+ /// return value should be checked.
Constant ensureIsSubtype(Constant constant, DartType type, TreeNode node) {
bool result;
switch (evaluationMode) {
@@ -2411,7 +2622,7 @@
bool weakResult = isSubtype(
weakConstant, type, SubtypeCheckMode.ignoringNullabilities);
if (strongResult != weakResult) {
- return report(node, messageNonAgnosticConstant);
+ return createErrorConstant(node, messageNonAgnosticConstant);
}
result = strongResult;
break;
@@ -2421,7 +2632,7 @@
break;
}
if (!result) {
- return report(
+ return createErrorConstant(
node,
templateConstEvalInvalidType.withArguments(constant, type,
constant.getType(_staticTypeContext), isNonNullableByDefault));
@@ -2429,47 +2640,93 @@
return constant;
}
- List<DartType> evaluateTypeArguments(TreeNode node, Arguments arguments) {
- return evaluateDartTypes(node, arguments.types);
+ /// Returns the types on success and null on failure.
+ /// Note that on failure an errorConstant is saved in [_gotError].
+ List<DartType> _evaluateTypeArguments(TreeNode node, Arguments arguments) {
+ return _evaluateDartTypes(node, arguments.types);
}
- List<DartType> evaluateSuperTypeArguments(TreeNode node, Supertype type) {
- return evaluateDartTypes(node, type.typeArguments);
+ /// Returns the types on success and null on failure.
+ /// Note that on failure an errorConstant is saved in [_gotError].
+ List<DartType> _evaluateSuperTypeArguments(TreeNode node, Supertype type) {
+ return _evaluateDartTypes(node, type.typeArguments);
}
- List<DartType> evaluateDartTypes(TreeNode node, List<DartType> types) {
+ /// Upon failure in certain procedure calls (e.g. [_evaluateDartTypes]) the
+ /// "error"-constant is saved here. Normally this should be null.
+ /// Once a caller calls such a procedure and it gives an error here,
+ /// the caller should fetch it an null-out this variable.
+ AbortConstant _gotError;
+
+ /// Returns the types on success and null on failure.
+ /// Note that on failure an errorConstant is saved in [_gotError].
+ List<DartType> _evaluateDartTypes(TreeNode node, List<DartType> types) {
// TODO: Once the frontend guarantees that there are no free type variables
// left over after substitution, we can enable this shortcut again:
// if (env.isEmpty) return types;
- return types.map((t) => evaluateDartType(node, t)).toList();
+ List<DartType> result =
+ new List<DartType>.filled(types.length, null, growable: true);
+ for (int i = 0; i < types.length; i++) {
+ DartType type = _evaluateDartType(node, types[i]);
+ if (type == null && _gotError != null) {
+ return null;
+ }
+ assert(_gotError == null);
+ assert(type != null);
+ result[i] = type;
+ }
+ return result;
}
- DartType evaluateDartType(TreeNode node, DartType type) {
+ /// Returns the type on success and null on failure.
+ /// Note that on failure an errorConstant is saved in [_gotError].
+ DartType _evaluateDartType(TreeNode node, DartType type) {
final DartType result = env.substituteType(type);
if (!isInstantiated(result)) {
- return report(
+ _gotError = createErrorConstant(
node,
templateConstEvalFreeTypeParameter.withArguments(
type, isNonNullableByDefault));
+ return null;
}
return result;
}
- List<Constant> evaluatePositionalArguments(Arguments arguments) {
- return arguments.positional.map((Expression node) {
- return _evaluateSubexpression(node);
- }).toList();
+ /// Returns the types on success and null on failure.
+ /// Note that on failure an errorConstant is saved in [_gotError].
+ List<Constant> _evaluatePositionalArguments(Arguments arguments) {
+ List<Constant> result = new List<Constant>.filled(
+ arguments.positional.length, null,
+ growable: true);
+ for (int i = 0; i < arguments.positional.length; i++) {
+ Constant constant = _evaluateSubexpression(arguments.positional[i]);
+ if (constant is AbortConstant) {
+ _gotError = constant;
+ return null;
+ }
+ result[i] = constant;
+ }
+ return result;
}
- Map<String, Constant> evaluateNamedArguments(Arguments arguments) {
+ /// Returns the arguments on success and null on failure.
+ /// Note that on failure an errorConstant is saved in [_gotError].
+ Map<String, Constant> _evaluateNamedArguments(Arguments arguments) {
if (arguments.named.isEmpty) return const <String, Constant>{};
final Map<String, Constant> named = {};
arguments.named.forEach((NamedExpression pair) {
- named[pair.name] = _evaluateSubexpression(pair.value);
+ if (_gotError != null) return null;
+ Constant constant = _evaluateSubexpression(pair.value);
+ if (constant is AbortConstant) {
+ _gotError = constant;
+ return null;
+ }
+ named[pair.name] = constant;
});
+ if (_gotError != null) return null;
return named;
}
@@ -2528,7 +2785,7 @@
return new DoubleConstant(a / b);
case '~/':
if (b == 0) {
- return report(
+ return createErrorConstant(
node, templateConstEvalZeroDivisor.withArguments(op, '$a'));
}
return intFolder.truncatingDivide(node, a, b);
@@ -2548,7 +2805,8 @@
}
// Probably unreachable.
- return reportInvalid(node, "Unexpected binary numeric operation '$op'.");
+ return createInvalidExpressionConstant(
+ node, "Unexpected binary numeric operation '$op'.");
}
Library libraryOf(TreeNode node) {
@@ -2685,20 +2943,121 @@
}
}
-// Used as control-flow to abort the current evaluation.
-class _AbortDueToError {
+abstract class AbortConstant implements Constant {}
+
+class _AbortDueToErrorConstant extends AbortConstant {
final TreeNode node;
final Message message;
final List<LocatedMessage> context;
- _AbortDueToError(this.node, this.message, {this.context});
+ _AbortDueToErrorConstant(this.node, this.message, {this.context});
+
+ @override
+ R accept<R>(ConstantVisitor<R> v) {
+ throw new UnimplementedError();
+ }
+
+ @override
+ R acceptReference<R>(Visitor<R> v) {
+ throw new UnimplementedError();
+ }
+
+ @override
+ Expression asExpression() {
+ throw new UnimplementedError();
+ }
+
+ @override
+ DartType getType(StaticTypeContext context) {
+ throw new UnimplementedError();
+ }
+
+ @override
+ String leakingDebugToString() {
+ throw new UnimplementedError();
+ }
+
+ @override
+ String toString() {
+ throw new UnimplementedError();
+ }
+
+ @override
+ String toStringInternal() {
+ throw new UnimplementedError();
+ }
+
+ @override
+ String toText(AstTextStrategy strategy) {
+ throw new UnimplementedError();
+ }
+
+ @override
+ void toTextInternal(AstPrinter printer) {
+ throw new UnimplementedError();
+ }
+
+ @override
+ void visitChildren(Visitor<dynamic> v) {
+ throw new UnimplementedError();
+ }
}
-class _AbortDueToInvalidExpression {
+class _AbortDueToInvalidExpressionConstant extends AbortConstant {
final TreeNode node;
final String message;
- _AbortDueToInvalidExpression(this.node, this.message);
+ _AbortDueToInvalidExpressionConstant(this.node, this.message);
+
+ @override
+ R accept<R>(ConstantVisitor<R> v) {
+ throw new UnimplementedError();
+ }
+
+ @override
+ R acceptReference<R>(Visitor<R> v) {
+ throw new UnimplementedError();
+ }
+
+ @override
+ Expression asExpression() {
+ throw new UnimplementedError();
+ }
+
+ @override
+ DartType getType(StaticTypeContext context) {
+ throw new UnimplementedError();
+ }
+
+ @override
+ String leakingDebugToString() {
+ throw new UnimplementedError();
+ }
+
+ @override
+ String toString() {
+ throw new UnimplementedError();
+ }
+
+ @override
+ String toStringInternal() {
+ throw new UnimplementedError();
+ }
+
+ @override
+ String toText(AstTextStrategy strategy) {
+ throw new UnimplementedError();
+ }
+
+ @override
+ void toTextInternal(AstPrinter printer) {
+ throw new UnimplementedError();
+ }
+
+ @override
+ void visitChildren(Visitor<dynamic> v) {
+ throw new UnimplementedError();
+ }
}
abstract class ErrorReporter {
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_int_folder.dart b/pkg/front_end/lib/src/fasta/kernel/constant_int_folder.dart
index 367ca58..0d73f92 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_int_folder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_int_folder.dart
@@ -41,15 +41,19 @@
Constant truncatingDivide(MethodInvocation node, num left, num right);
- void _checkOperands(MethodInvocation node, String op, num left, num right) {
+ /// Returns [null] on success and an error-"constant" on failure, as such the
+ /// return value should be checked.
+ AbortConstant _checkOperands(
+ MethodInvocation node, String op, num left, num right) {
if ((op == '<<' || op == '>>' || op == '>>>') && right < 0) {
- evaluator.report(node,
+ return evaluator.createErrorConstant(node,
templateConstEvalNegativeShift.withArguments(op, '$left', '$right'));
}
if ((op == '%' || op == '~/') && right == 0) {
- evaluator.report(
+ return evaluator.createErrorConstant(
node, templateConstEvalZeroDivisor.withArguments(op, '$left'));
}
+ return null;
}
}
@@ -74,7 +78,8 @@
return new IntConstant(~operand.value);
default:
// Probably unreachable.
- return evaluator.reportInvalid(node, "Invalid unary operator $op");
+ return evaluator.createInvalidExpressionConstant(
+ node, "Invalid unary operator $op");
}
}
@@ -83,7 +88,8 @@
MethodInvocation node, String op, IntConstant left, IntConstant right) {
int a = left.value;
int b = right.value;
- _checkOperands(node, op, a, b);
+ AbortConstant error = _checkOperands(node, op, a, b);
+ if (error != null) return error;
switch (op) {
case '+':
return new IntConstant(a + b);
@@ -121,7 +127,8 @@
return evaluator.makeBoolConstant(a > b);
default:
// Probably unreachable.
- return evaluator.reportInvalid(node, "Invalid binary operator $op");
+ return evaluator.createInvalidExpressionConstant(
+ node, "Invalid binary operator $op");
}
}
@@ -130,7 +137,7 @@
try {
return new IntConstant(left ~/ right);
} catch (e) {
- return evaluator.report(node,
+ return evaluator.createErrorConstant(node,
templateConstEvalTruncateError.withArguments('$left', '$right'));
}
}
@@ -176,7 +183,8 @@
return new DoubleConstant(_truncate32(~intValue).toDouble());
default:
// Probably unreachable.
- return evaluator.reportInvalid(node, "Invalid unary operator $op");
+ return evaluator.createInvalidExpressionConstant(
+ node, "Invalid unary operator $op");
}
}
@@ -185,7 +193,8 @@
DoubleConstant left, DoubleConstant right) {
double a = left.value;
double b = right.value;
- _checkOperands(node, op, a, b);
+ AbortConstant error = _checkOperands(node, op, a, b);
+ if (error != null) return error;
switch (op) {
case '+':
return new DoubleConstant(a + b);
@@ -229,7 +238,8 @@
return evaluator.makeBoolConstant(a > b);
default:
// Probably unreachable.
- return evaluator.reportInvalid(node, "Invalid binary operator $op");
+ return evaluator.createInvalidExpressionConstant(
+ node, "Invalid binary operator $op");
}
}
@@ -237,7 +247,7 @@
Constant truncatingDivide(MethodInvocation node, num left, num right) {
double division = (left / right);
if (division.isNaN || division.isInfinite) {
- return evaluator.report(node,
+ return evaluator.createErrorConstant(node,
templateConstEvalTruncateError.withArguments('$left', '${right}'));
}
double result = division.truncateToDouble();
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index bae6f0b..17ed18c 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -757,6 +757,7 @@
overloader
overlooked
overshadowed
+oversight
overwrite
overwriting
ownership
diff --git a/pkg/front_end/testcases/late_lowering/late_final_nullable_local_without_initializer.dart.strong.transformed.expect b/pkg/front_end/testcases/late_lowering/late_final_nullable_local_without_initializer.dart.strong.transformed.expect
index 3d8ddf1..1f02ca5 100644
--- a/pkg/front_end/testcases/late_lowering/late_final_nullable_local_without_initializer.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_final_nullable_local_without_initializer.dart.strong.transformed.expect
@@ -56,3 +56,9 @@
}
throw "${message}: ${value}";
}
+
+
+Extra constant evaluation status:
+Evaluated: MethodInvocation @ org-dartlang-testcase:///late_final_nullable_local_without_initializer.dart:12:9 -> BoolConstant(true)
+Evaluated: MethodInvocation @ org-dartlang-testcase:///late_final_nullable_local_without_initializer.dart:26:11 -> BoolConstant(true)
+Extra constant evaluation: tries: 81, successes: 2
diff --git a/pkg/front_end/testcases/late_lowering/late_final_nullable_local_without_initializer.dart.weak.transformed.expect b/pkg/front_end/testcases/late_lowering/late_final_nullable_local_without_initializer.dart.weak.transformed.expect
index 3d8ddf1..1f02ca5 100644
--- a/pkg/front_end/testcases/late_lowering/late_final_nullable_local_without_initializer.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/late_lowering/late_final_nullable_local_without_initializer.dart.weak.transformed.expect
@@ -56,3 +56,9 @@
}
throw "${message}: ${value}";
}
+
+
+Extra constant evaluation status:
+Evaluated: MethodInvocation @ org-dartlang-testcase:///late_final_nullable_local_without_initializer.dart:12:9 -> BoolConstant(true)
+Evaluated: MethodInvocation @ org-dartlang-testcase:///late_final_nullable_local_without_initializer.dart:26:11 -> BoolConstant(true)
+Extra constant evaluation: tries: 81, successes: 2