Skip to content

Commit 4c48062

Browse files
askeksacommit-bot@chromium.org
authored andcommitted
[CFE] Fail silently on invalid constructs during constant evaluation.
This acts as a stable backstop for situations where Fasta fails to rewrite various erroneous constructs into invalid expressions. Change-Id: Ibaac3d62b9cfdc597c0f85aadf9aec2920689222 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97511 Reviewed-by: Kevin Millikin <[email protected]> Commit-Queue: Aske Simon Christensen <[email protected]>
1 parent a8e51c5 commit 4c48062

File tree

4 files changed

+86
-24
lines changed

4 files changed

+86
-24
lines changed

pkg/front_end/lib/src/fasta/fasta_codes_generated.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,38 @@ Message _withArgumentsConstEvalInvalidMethodInvocation(
12141214
arguments: {'string': string, 'constant': _constant});
12151215
}
12161216

1217+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1218+
const Template<
1219+
Message Function(
1220+
String string,
1221+
Constant
1222+
_constant)> templateConstEvalInvalidPropertyGet = const Template<
1223+
Message Function(String string, Constant _constant)>(
1224+
messageTemplate:
1225+
r"""The property '#string' can't be accessed on '#constant' within a const context.""",
1226+
withArguments: _withArgumentsConstEvalInvalidPropertyGet);
1227+
1228+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1229+
const Code<Message Function(String string, Constant _constant)>
1230+
codeConstEvalInvalidPropertyGet =
1231+
const Code<Message Function(String string, Constant _constant)>(
1232+
"ConstEvalInvalidPropertyGet", templateConstEvalInvalidPropertyGet,
1233+
analyzerCodes: <String>["CONST_EVAL_THROWS_EXCEPTION"]);
1234+
1235+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1236+
Message _withArgumentsConstEvalInvalidPropertyGet(
1237+
String string, Constant _constant) {
1238+
if (string.isEmpty) throw 'No string provided';
1239+
TypeLabeler labeler = new TypeLabeler();
1240+
List<Object> constantParts = labeler.labelConstant(_constant);
1241+
String constant = constantParts.join();
1242+
return new Message(codeConstEvalInvalidPropertyGet,
1243+
message:
1244+
"""The property '${string}' can't be accessed on '${constant}' within a const context.""" +
1245+
labeler.originMessages,
1246+
arguments: {'string': string, 'constant': _constant});
1247+
}
1248+
12171249
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
12181250
const Template<
12191251
Message Function(

pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import '../fasta_codes.dart'
4848
templateConstEvalInvalidType,
4949
templateConstEvalInvalidBinaryOperandType,
5050
templateConstEvalInvalidMethodInvocation,
51+
templateConstEvalInvalidPropertyGet,
5152
templateConstEvalInvalidStaticInvocation,
5253
templateConstEvalInvalidStringInterpolationOperand,
5354
templateConstEvalInvalidSymbolName,
@@ -478,17 +479,27 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
478479
errorReporter.report(locatedMessage, contextMessages);
479480
return new UnevaluatedConstant(new InvalidExpression(e.message.message));
480481
} on _AbortDueToInvalidExpression catch (e) {
481-
errorReporter.reportInvalidExpression(e.node);
482-
return new UnevaluatedConstant(e.node);
482+
// TODO(askesc): Copy position from erroneous node.
483+
// Currently not possible, as it might be in a different file.
484+
// Can be done if we add an explicit URI to InvalidExpression.
485+
InvalidExpression invalid = new InvalidExpression(e.message);
486+
if (invalid.fileOffset == TreeNode.noOffset) {
487+
invalid.fileOffset = node.fileOffset;
488+
}
489+
errorReporter.reportInvalidExpression(invalid);
490+
return new UnevaluatedConstant(invalid);
483491
}
484492
}
485493

494+
/// Report an error that has been detected during constant evaluation.
486495
Null report(TreeNode node, Message message, {List<LocatedMessage> context}) {
487496
throw new _AbortDueToError(node, message, context: context);
488497
}
489498

490-
Null reportInvalidExpression(InvalidExpression node) {
491-
throw new _AbortDueToInvalidExpression(node);
499+
/// Report a construct that should not occur inside a potentially constant
500+
/// expression. It is assumed that an error has already been reported.
501+
Null reportInvalid(TreeNode node, String message) {
502+
throw new _AbortDueToInvalidExpression(node, message);
492503
}
493504

494505
/// Produce an unevaluated constant node for an expression.
@@ -575,7 +586,8 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
575586
defaultTreeNode(Node node) {
576587
// Only a subset of the expression language is valid for constant
577588
// evaluation.
578-
throw 'Constant evaluation has no support for ${node.runtimeType} yet!';
589+
return reportInvalid(
590+
node, 'Constant evaluation has no support for ${node.runtimeType}!');
579591
}
580592

581593
visitNullLiteral(NullLiteral node) => nullConstant;
@@ -707,15 +719,18 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
707719
final Constructor constructor = node.target;
708720
final Class klass = constructor.enclosingClass;
709721
if (!constructor.isConst) {
710-
throw 'The front-end should ensure we do not encounter a '
711-
'constructor invocation of a non-const constructor.';
722+
return reportInvalid(node, 'Non-const constructor invocation.');
712723
}
713724
if (constructor.function.body != null &&
714725
constructor.function.body is! EmptyStatement) {
715-
throw 'Constructor "$node" has non-trivial body "${constructor.function.body.runtimeType}".';
726+
return reportInvalid(
727+
node,
728+
'Constructor "$node" has non-trivial body '
729+
'"${constructor.function.body.runtimeType}".');
716730
}
717731
if (klass.isAbstract) {
718-
throw 'Constructor "$node" belongs to abstract class "${klass}".';
732+
return reportInvalid(
733+
node, 'Constructor "$node" belongs to abstract class "${klass}".');
719734
}
720735

721736
final positionals = evaluatePositionalArguments(node.arguments);
@@ -993,16 +1008,18 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
9931008
}
9941009
}
9951010
} else {
996-
throw new Exception(
997-
'No support for handling initializer of type "${init.runtimeType}".');
1011+
return reportInvalid(
1012+
constructor,
1013+
'No support for handling initializer of type '
1014+
'"${init.runtimeType}".');
9981015
}
9991016
}
10001017
});
10011018
});
10021019
}
10031020

10041021
visitInvalidExpression(InvalidExpression node) {
1005-
return reportInvalidExpression(node);
1022+
return reportInvalid(node, node.message);
10061023
}
10071024

10081025
visitMethodInvocation(MethodInvocation node) {
@@ -1260,12 +1277,16 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
12601277
visitPropertyGet(PropertyGet node) {
12611278
if (node.receiver is ThisExpression) {
12621279
// Access "this" during instance creation.
1280+
if (instanceBuilder == null) {
1281+
return reportInvalid(node, 'Instance field access outside constructor');
1282+
}
12631283
for (final Field field in instanceBuilder.fields.keys) {
12641284
if (field.name == node.name) {
12651285
return instanceBuilder.fields[field];
12661286
}
12671287
}
1268-
throw 'Could not evaluate field get ${node.name} on incomplete instance';
1288+
return reportInvalid(node,
1289+
'Could not evaluate field get ${node.name} on incomplete instance');
12691290
}
12701291

12711292
final Constant receiver = _evaluateSubexpression(node.receiver);
@@ -1283,7 +1304,10 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
12831304
} else if (receiver is NullConstant) {
12841305
return report(node, messageConstEvalNullValue);
12851306
}
1286-
throw 'Could not evaluate property get on $receiver.';
1307+
return report(
1308+
node,
1309+
templateConstEvalInvalidPropertyGet.withArguments(
1310+
node.name.name, receiver));
12871311
}
12881312

12891313
visitLet(Let node) {
@@ -1310,8 +1334,7 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
13101334
if (variable.isConst) {
13111335
return _evaluateSubexpression(variable.initializer);
13121336
}
1313-
throw new Exception('The front-end should ensure we do not encounter a '
1314-
'variable get of a non-const variable.');
1337+
return reportInvalid(node, 'Variable get of a non-const variable.');
13151338
}
13161339

13171340
visitStaticGet(StaticGet node) {
@@ -1340,8 +1363,8 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
13401363
templateConstEvalInvalidStaticInvocation
13411364
.withArguments(target.name.name));
13421365
} else {
1343-
throw new Exception(
1344-
'No support for ${target.runtimeType} in a static-get.');
1366+
reportInvalid(
1367+
node, 'No support for ${target.runtimeType} in a static-get.');
13451368
}
13461369
});
13471370
}
@@ -1530,14 +1553,15 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
15301553
return canonicalize(
15311554
new PartialInstantiationConstant(constant, typeArguments));
15321555
}
1533-
throw new Exception(
1556+
return reportInvalid(
1557+
node,
15341558
'The number of type arguments supplied in the partial instantiation '
15351559
'does not match the number of type arguments of the $constant.');
15361560
}
15371561
// The inner expression in an instantiation can never be null, since
15381562
// instantiations are only inferred on direct references to declarations.
1539-
throw new Exception(
1540-
'Only tear-off constants can be partially instantiated.');
1563+
return reportInvalid(
1564+
node, 'Only tear-off constants can be partially instantiated.');
15411565
}
15421566

15431567
@override
@@ -1701,7 +1725,7 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
17011725
return a > b ? trueConstant : falseConstant;
17021726
}
17031727

1704-
throw new Exception("Unexpected binary numeric operation '$op'.");
1728+
return reportInvalid(node, "Unexpected binary numeric operation '$op'.");
17051729
}
17061730

17071731
Library libraryOf(TreeNode node) {
@@ -1793,9 +1817,10 @@ class _AbortDueToError {
17931817
}
17941818

17951819
class _AbortDueToInvalidExpression {
1796-
final InvalidExpression node;
1820+
final TreeNode node;
1821+
final String message;
17971822

1798-
_AbortDueToInvalidExpression(this.node);
1823+
_AbortDueToInvalidExpression(this.node, this.message);
17991824
}
18001825

18011826
abstract class ErrorReporter {

pkg/front_end/messages.status

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ ConstEvalFreeTypeParameter/example: Fail
7272
ConstEvalInvalidBinaryOperandType/analyzerCode: Fail # CONST_EVAL_TYPE_NUM / CONST_EVAL_TYPE_BOOL
7373
ConstEvalInvalidBinaryOperandType/example: Fail
7474
ConstEvalInvalidMethodInvocation/example: Fail
75+
ConstEvalInvalidPropertyGet/example: Fail
7576
ConstEvalInvalidStaticInvocation/example: Fail
7677
ConstEvalInvalidStringInterpolationOperand/example: Fail
7778
ConstEvalInvalidSymbolName/example: Fail

pkg/front_end/messages.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ ConstEvalInvalidMethodInvocation:
110110
template: "The method '#string' can't be invoked on '#constant' within a const context."
111111
analyzerCode: UNDEFINED_OPERATOR
112112

113+
ConstEvalInvalidPropertyGet:
114+
template: "The property '#string' can't be accessed on '#constant' within a const context."
115+
analyzerCode: CONST_EVAL_THROWS_EXCEPTION
116+
113117
ConstEvalInvalidStringInterpolationOperand:
114118
template: "The '#constant' can't be used as part of a string interpolation within a const context, only values of type 'null', 'bool', 'int', 'double', or 'String' can be used."
115119
analyzerCode: CONST_EVAL_TYPE_BOOL_NUM_STRING

0 commit comments

Comments
 (0)