[vm/kernel] Enable kernel2kernel "constants" transformation in AOT mode (after running TFA)
This CL also uses the newer `onProblem` error reporting mechanism, which supports contexts.
The errors by the constant evaluator are formatted e.g. like this:
.../language_2/compile_time_constant_o_test_01.dart:14:8: Error: Duplicate keys are not allowed in constant maps (found duplicate key "StringConstant(foo)").
"foo": 499
^
.../language_2/compile_time_constant_o_test_01.dart:32:24: Context: While analyzing:
Expect.identical(m1, m3);
Change-Id: I463416e14686e218b0f08903bd6aa0bca7392260
Reviewed-on: https://dart-review.googlesource.com/53021
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes.dart b/pkg/front_end/lib/src/fasta/fasta_codes.dart
index 3d1368d..6e7dbc1 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes.dart
@@ -4,7 +4,7 @@
library fasta.codes;
-import 'package:kernel/ast.dart' show DartType;
+import 'package:kernel/ast.dart' show Constant, DartType;
import 'package:kernel/text/ast_to_text.dart' show NameSystem, Printer;
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index ae67c8e..df547c2 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -726,6 +726,257 @@
tip: r"""Try removing either the 'const' keyword or the body.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeConstEvalContext = messageConstEvalContext;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageConstEvalContext =
+ const MessageCode("ConstEvalContext", message: r"""While analyzing:""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+ Message Function(
+ Constant
+ constant)> templateConstEvalDuplicateKey = const Template<
+ Message Function(Constant constant)>(
+ messageTemplate:
+ r"""The key '#constant' conflicts with another existing key in the map.""",
+ withArguments: _withArgumentsConstEvalDuplicateKey);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(Constant constant)> codeConstEvalDuplicateKey =
+ const Code<Message Function(Constant constant)>(
+ "ConstEvalDuplicateKey", templateConstEvalDuplicateKey,
+ analyzerCode: "EQUAL_KEYS_IN_MAP");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalDuplicateKey(Constant constant) {
+ return new Message(codeConstEvalDuplicateKey,
+ message:
+ """The key '$constant' conflicts with another existing key in the map.""",
+ arguments: {'constant': constant});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeConstEvalFailedAssertion = messageConstEvalFailedAssertion;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageConstEvalFailedAssertion = const MessageCode(
+ "ConstEvalFailedAssertion",
+ analyzerCode: "CONST_EVAL_THROWS_EXCEPTION",
+ message: r"""This assertion failed.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String string)>
+ templateConstEvalFailedAssertionWithMessage =
+ const Template<Message Function(String string)>(
+ messageTemplate: r"""This assertion failed with message: #string""",
+ withArguments: _withArgumentsConstEvalFailedAssertionWithMessage);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string)>
+ codeConstEvalFailedAssertionWithMessage =
+ const Code<Message Function(String string)>(
+ "ConstEvalFailedAssertionWithMessage",
+ templateConstEvalFailedAssertionWithMessage,
+ analyzerCode: "CONST_EVAL_THROWS_EXCEPTION");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalFailedAssertionWithMessage(String string) {
+ return new Message(codeConstEvalFailedAssertionWithMessage,
+ message: """This assertion failed with message: $string""",
+ arguments: {'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+ Message Function(
+ String string,
+ Constant constant,
+ DartType _type,
+ DartType
+ _type2)> templateConstEvalInvalidBinaryOperandType = const Template<
+ Message Function(String string, Constant constant, DartType _type,
+ DartType _type2)>(
+ messageTemplate:
+ r"""Binary operator '#string' on '#constant' requires operand of type '#type', but was of type '#type2'.""",
+ withArguments: _withArgumentsConstEvalInvalidBinaryOperandType);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+ Message Function(
+ String string, Constant constant, DartType _type, DartType _type2)>
+ codeConstEvalInvalidBinaryOperandType = const Code<
+ Message Function(
+ String string, Constant constant, DartType _type, DartType _type2)>(
+ "ConstEvalInvalidBinaryOperandType",
+ templateConstEvalInvalidBinaryOperandType,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalInvalidBinaryOperandType(
+ String string, Constant constant, DartType _type, DartType _type2) {
+ NameSystem nameSystem = new NameSystem();
+ StringBuffer buffer = new StringBuffer();
+ new Printer(buffer, syntheticNames: nameSystem).writeNode(_type);
+ String type = '$buffer';
+
+ buffer = new StringBuffer();
+ new Printer(buffer, syntheticNames: nameSystem).writeNode(_type2);
+ String type2 = '$buffer';
+
+ return new Message(codeConstEvalInvalidBinaryOperandType,
+ message:
+ """Binary operator '$string' on '$constant' requires operand of type '$type', but was of type '$type2'.""",
+ arguments: {
+ 'string': string,
+ 'constant': constant,
+ 'type': _type,
+ 'type2': _type2
+ });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+ Message Function(
+ String string,
+ Constant
+ constant)> templateConstEvalInvalidMethodInvocation = const Template<
+ Message Function(String string, Constant constant)>(
+ messageTemplate:
+ r"""The method '#string' can't be invoked on '#constant' within a const context.""",
+ withArguments: _withArgumentsConstEvalInvalidMethodInvocation);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string, Constant constant)>
+ codeConstEvalInvalidMethodInvocation =
+ const Code<Message Function(String string, Constant constant)>(
+ "ConstEvalInvalidMethodInvocation",
+ templateConstEvalInvalidMethodInvocation,
+ analyzerCode: "UNDEFINED_OPERATOR");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalInvalidMethodInvocation(
+ String string, Constant constant) {
+ return new Message(codeConstEvalInvalidMethodInvocation,
+ message:
+ """The method '$string' can't be invoked on '$constant' within a const context.""",
+ arguments: {'string': string, 'constant': constant});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+ Message Function(
+ String
+ name)> templateConstEvalInvalidStaticInvocation = const Template<
+ Message Function(String name)>(
+ messageTemplate:
+ r"""The invocation of '#name' is not allowed within a const context.""",
+ withArguments: _withArgumentsConstEvalInvalidStaticInvocation);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)> codeConstEvalInvalidStaticInvocation =
+ const Code<Message Function(String name)>(
+ "ConstEvalInvalidStaticInvocation",
+ templateConstEvalInvalidStaticInvocation,
+ analyzerCode: "CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalInvalidStaticInvocation(String name) {
+ return new Message(codeConstEvalInvalidStaticInvocation,
+ message:
+ """The invocation of '$name' is not allowed within a const context.""",
+ arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(Constant constant)>
+ templateConstEvalInvalidStringInterpolationOperand =
+ const Template<Message Function(Constant constant)>(
+ messageTemplate:
+ r"""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.""",
+ withArguments:
+ _withArgumentsConstEvalInvalidStringInterpolationOperand);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(Constant constant)>
+ codeConstEvalInvalidStringInterpolationOperand =
+ const Code<Message Function(Constant constant)>(
+ "ConstEvalInvalidStringInterpolationOperand",
+ templateConstEvalInvalidStringInterpolationOperand,
+ analyzerCode: "CONST_EVAL_TYPE_BOOL_NUM_STRING");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalInvalidStringInterpolationOperand(
+ Constant constant) {
+ return new Message(codeConstEvalInvalidStringInterpolationOperand,
+ message:
+ """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.""",
+ arguments: {'constant': constant});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+ Message Function(
+ Constant constant,
+ DartType _type,
+ DartType
+ _type2)> templateConstEvalInvalidType = const Template<
+ Message Function(Constant constant, DartType _type, DartType _type2)>(
+ messageTemplate:
+ r"""Expected constant '#constant' to be of type '#type', but was of type '#type2'.""",
+ withArguments: _withArgumentsConstEvalInvalidType);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(Constant constant, DartType _type, DartType _type2)>
+ codeConstEvalInvalidType = const Code<
+ Message Function(Constant constant, DartType _type, DartType _type2)>(
+ "ConstEvalInvalidType",
+ templateConstEvalInvalidType,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalInvalidType(
+ Constant constant, DartType _type, DartType _type2) {
+ NameSystem nameSystem = new NameSystem();
+ StringBuffer buffer = new StringBuffer();
+ new Printer(buffer, syntheticNames: nameSystem).writeNode(_type);
+ String type = '$buffer';
+
+ buffer = new StringBuffer();
+ new Printer(buffer, syntheticNames: nameSystem).writeNode(_type2);
+ String type2 = '$buffer';
+
+ return new Message(codeConstEvalInvalidType,
+ message:
+ """Expected constant '$constant' to be of type '$type', but was of type '$type2'.""",
+ arguments: {'constant': constant, 'type': _type, 'type2': _type2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+ Message Function(
+ String
+ string)> templateConstEvalNonConstantLiteral = const Template<
+ Message Function(String string)>(
+ messageTemplate:
+ r"""Can't have a non-constant #string literal within a const context.""",
+ withArguments: _withArgumentsConstEvalNonConstantLiteral);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string)> codeConstEvalNonConstantLiteral =
+ const Code<Message Function(String string)>(
+ "ConstEvalNonConstantLiteral", templateConstEvalNonConstantLiteral,
+ analyzerCode: "NON_CONSTANT_DEFAULT_VALUE");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalNonConstantLiteral(String string) {
+ return new Message(codeConstEvalNonConstantLiteral,
+ message:
+ """Can't have a non-constant $string literal within a const context.""",
+ arguments: {'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeConstFactory = messageConstFactory;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 692b8d5..5e79fc9 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -61,6 +61,26 @@
ConstAndVar/script1: Fail
ConstConstructorNonFinalField/analyzerCode: Fail
ConstConstructorNonFinalField/example: Fail
+ConstEvalContext/analyzerCode: Fail # This is just used for displaying the context.
+ConstEvalContext/example: Fail # This is just used for displaying the context.
+ConstEvalDuplicateKey/dart2jsCode: Fail
+ConstEvalDuplicateKey/example: Fail
+ConstEvalFailedAssertion/dart2jsCode: Fail
+ConstEvalFailedAssertion/example: Fail
+ConstEvalFailedAssertionWithMessage/dart2jsCode: Fail
+ConstEvalFailedAssertionWithMessage/example: Fail
+ConstEvalInvalidBinaryOperandType/analyzerCode: Fail # CONST_EVAL_TYPE_NUM / CONST_EVAL_TYPE_BOOL
+ConstEvalInvalidBinaryOperandType/example: Fail
+ConstEvalInvalidMethodInvocation/dart2jsCode: Fail
+ConstEvalInvalidMethodInvocation/example: Fail
+ConstEvalInvalidStaticInvocation/dart2jsCode: Fail
+ConstEvalInvalidStaticInvocation/example: Fail
+ConstEvalInvalidStringInterpolationOperand/dart2jsCode: Fail
+ConstEvalInvalidStringInterpolationOperand/example: Fail
+ConstEvalInvalidType/analyzerCode: Fail # CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH / CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH / CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH / ...
+ConstEvalInvalidType/example: Fail
+ConstEvalNonConstantLiteral/dart2jsCode: Fail
+ConstEvalNonConstantLiteral/example: Fail
ConstFieldWithoutInitializer/example: Fail
ConstructorHasNoSuchNamedParameter/analyzerCode: Fail
ConstructorHasNoSuchNamedParameter/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 932840e..696070c 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -72,6 +72,43 @@
analyzerCode: ILLEGAL_CHARACTER
expression: "\x1b 1"
+ConstEvalContext:
+ template: "While analyzing:"
+
+ConstEvalDuplicateKey:
+ template: "The key '#constant' conflicts with another existing key in the map."
+ analyzerCode: EQUAL_KEYS_IN_MAP
+
+ConstEvalNonConstantLiteral:
+ template: "Can't have a non-constant #string literal within a const context."
+ analyzerCode: NON_CONSTANT_DEFAULT_VALUE
+
+ConstEvalInvalidType:
+ template: "Expected constant '#constant' to be of type '#type', but was of type '#type2'."
+
+ConstEvalInvalidBinaryOperandType:
+ template: "Binary operator '#string' on '#constant' requires operand of type '#type', but was of type '#type2'."
+
+ConstEvalInvalidMethodInvocation:
+ template: "The method '#string' can't be invoked on '#constant' within a const context."
+ analyzerCode: UNDEFINED_OPERATOR
+
+ConstEvalInvalidStringInterpolationOperand:
+ 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."
+ analyzerCode: CONST_EVAL_TYPE_BOOL_NUM_STRING
+
+ConstEvalInvalidStaticInvocation:
+ template: "The invocation of '#name' is not allowed within a const context."
+ analyzerCode: CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+
+ConstEvalFailedAssertion:
+ template: "This assertion failed."
+ analyzerCode: CONST_EVAL_THROWS_EXCEPTION
+
+ConstEvalFailedAssertionWithMessage:
+ template: "This assertion failed with message: #string"
+ analyzerCode: CONST_EVAL_THROWS_EXCEPTION
+
NonAsciiIdentifier:
template: "The non-ASCII character '#character' (#unicode) can't be used in identifiers, only in strings and comments."
tip: "Try using an US-ASCII letter, a digit, '_' (an underscore), or '$' (a dollar sign)."
diff --git a/pkg/front_end/tool/_fasta/generate_messages.dart b/pkg/front_end/tool/_fasta/generate_messages.dart
index 0ec9a18..bae14ba 100644
--- a/pkg/front_end/tool/_fasta/generate_messages.dart
+++ b/pkg/front_end/tool/_fasta/generate_messages.dart
@@ -172,6 +172,11 @@
arguments.add("'count2': count2");
break;
+ case "#constant":
+ parameters.add("Constant constant");
+ arguments.add("'constant': constant");
+ break;
+
default:
throw "Unhandled placeholder in template: ${match[0]}";
}
diff --git a/pkg/kernel/lib/transformations/constants.dart b/pkg/kernel/lib/transformations/constants.dart
index 2c55885..d44418f 100644
--- a/pkg/kernel/lib/transformations/constants.dart
+++ b/pkg/kernel/lib/transformations/constants.dart
@@ -409,9 +409,7 @@
defaultTreeNode(Node node) {
// Only a subset of the expression language is valid for constant
// evaluation.
- errorReporter.unimplemented(contextChain, node,
- 'Constant evaluation has no support for ${node.runtimeType} yet!');
- throw const _AbortCurrentEvaluation();
+ throw 'Constant evaluation has no support for ${node.runtimeType} yet!';
}
visitNullLiteral(NullLiteral node) => nullConstant;
@@ -449,7 +447,7 @@
visitListLiteral(ListLiteral node) {
if (!node.isConst) {
- errorReporter.nonConstantLiteral(contextChain, node, 'List');
+ errorReporter.nonConstLiteral(contextChain, node, 'List');
throw const _AbortCurrentEvaluation();
}
final List<Constant> entries = new List<Constant>(node.expressions.length);
@@ -463,7 +461,7 @@
visitMapLiteral(MapLiteral node) {
if (!node.isConst) {
- errorReporter.nonConstantLiteral(contextChain, node, 'Map');
+ errorReporter.nonConstLiteral(contextChain, node, 'Map');
throw const _AbortCurrentEvaluation();
}
final Set<Constant> usedKeys = new Set<Constant>();
@@ -473,6 +471,9 @@
final key = node.entries[i].key.accept(this);
final value = node.entries[i].value.accept(this);
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.
errorReporter.duplicateKey(contextChain, node.entries[i], key);
throw const _AbortCurrentEvaluation();
}
@@ -485,23 +486,24 @@
return canonicalize(backend.lowerMapConstant(mapConstant));
}
+ visitFunctionExpression(FunctionExpression node) {
+ errorReporter.nonConstLiteral(contextChain, node, 'Function');
+ throw const _AbortCurrentEvaluation();
+ }
+
visitConstructorInvocation(ConstructorInvocation node) {
final Constructor constructor = node.target;
final Class klass = constructor.enclosingClass;
if (!constructor.isConst) {
- errorReporter.nonConstConstructorInvocation(
- contextChain, node, constructor);
- throw const _AbortCurrentEvaluation();
+ throw 'The front-end should ensure we do not encounter a '
+ 'constructor invocation of a non-const constructor.';
}
- if (constructor.function.body is! EmptyStatement) {
- errorReporter.unreachable(contextChain, node,
- 'Constructor "$node" has non-trivial body "${constructor.function.body.runtimeType}".');
- throw const _AbortCurrentEvaluation();
+ if (constructor.function.body != null &&
+ constructor.function.body is! EmptyStatement) {
+ throw 'Constructor "$node" has non-trivial body "${constructor.function.body.runtimeType}".';
}
if (klass.isAbstract) {
- errorReporter.unreachable(contextChain, node,
- 'Constructor "$node" belongs to abstract class "${klass}".');
- throw const _AbortCurrentEvaluation();
+ throw 'Constructor "$node" belongs to abstract class "${klass}".';
}
final typeArguments = evaluateTypeArguments(node.arguments);
@@ -587,20 +589,34 @@
if (condition is BoolConstant) {
if (!condition.value) {
final Constant message = init.statement.message?.accept(this);
- errorReporter.failedAssertion(
- contextChain, init.statement.condition, message);
+ if (message == null) {
+ errorReporter.failedAssertion(
+ contextChain, init.statement.condition, null);
+ throw const _AbortCurrentEvaluation();
+ } else if (message is StringConstant) {
+ errorReporter.failedAssertion(
+ contextChain, init.statement.condition, message.value);
+ throw const _AbortCurrentEvaluation();
+ }
+ errorReporter.invalidDartType(
+ contextChain,
+ init.statement.message,
+ message,
+ typeEnvironment.stringType);
throw const _AbortCurrentEvaluation();
}
} else {
- errorReporter.invalidType(
- contextChain, init.statement.condition, condition, 'bool');
+ errorReporter.invalidDartType(
+ contextChain,
+ init.statement.condition,
+ condition,
+ typeEnvironment.boolType);
throw const _AbortCurrentEvaluation();
}
}
} else {
- errorReporter.unimplemented(contextChain, init,
+ throw new Exception(
'No support for handling initializer of type "${init.runtimeType}".');
- throw const _AbortCurrentEvaluation();
}
}
});
@@ -615,21 +631,16 @@
final List<Constant> arguments =
evaluatePositionalArguments(node.arguments);
+ // TODO(http://dartbug.com/31799): Ensure we only invoke ==/!= on
+ // null/bool/int/double/String objects.
+
// Handle == and != first (it's common between all types).
if (arguments.length == 1 && node.name.name == '==') {
- // TODO(http://dartbug.com/31799): Re-enable these checks.
- //ensurePrimitiveConstant(receiver);
final right = arguments[0];
- // TODO(http://dartbug.com/31799): Re-enable these checks.
- //ensurePrimitiveConstant(right);
return receiver == right ? trueConstant : falseConstant;
}
if (arguments.length == 1 && node.name.name == '!=') {
- // TODO(http://dartbug.com/31799): Re-enable these checks.
- //ensurePrimitiveConstant(receiver);
final right = arguments[0];
- // TODO(http://dartbug.com/31799): Re-enable these checks.
- //ensurePrimitiveConstant(right);
return receiver != right ? trueConstant : falseConstant;
}
@@ -644,7 +655,12 @@
new StringConstant(receiver.value + other.value));
}
errorReporter.invalidBinaryOperandType(
- contextChain, node, 'String', '+', 'String', '$other');
+ contextChain,
+ node,
+ receiver,
+ '+',
+ typeEnvironment.stringType,
+ other.getType(typeEnvironment));
throw const _AbortCurrentEvaluation();
}
}
@@ -669,7 +685,12 @@
}
}
errorReporter.invalidBinaryOperandType(
- contextChain, node, 'bool', '${node.name.name}', 'bool', '$right');
+ contextChain,
+ node,
+ receiver,
+ '${node.name.name}',
+ typeEnvironment.boolType,
+ right.getType(typeEnvironment));
throw const _AbortCurrentEvaluation();
}
} else if (receiver is IntConstant) {
@@ -710,8 +731,13 @@
node.name.name, receiver.value, value, node);
}
- errorReporter.invalidBinaryOperandType(contextChain, node, 'int',
- '${node.name.name}', 'int/double', '$other');
+ errorReporter.invalidBinaryOperandType(
+ contextChain,
+ node,
+ receiver,
+ '${node.name.name}',
+ typeEnvironment.numType,
+ other.getType(typeEnvironment));
throw const _AbortCurrentEvaluation();
}
} else if (receiver is DoubleConstant) {
@@ -730,8 +756,13 @@
return evaluateBinaryNumericOperation(
node.name.name, receiver.value, value, node);
}
- errorReporter.invalidBinaryOperandType(contextChain, node, 'double',
- '${node.name.name}', 'int/double', '$other');
+ errorReporter.invalidBinaryOperandType(
+ contextChain,
+ node,
+ receiver,
+ '${node.name.name}',
+ typeEnvironment.numType,
+ other.getType(typeEnvironment));
throw const _AbortCurrentEvaluation();
}
}
@@ -752,7 +783,12 @@
return right;
}
errorReporter.invalidBinaryOperandType(
- contextChain, node, 'bool', '${node.operator}', 'bool', '$right');
+ contextChain,
+ node,
+ left,
+ '${node.operator}',
+ typeEnvironment.boolType,
+ right.getType(typeEnvironment));
throw const _AbortCurrentEvaluation();
}
errorReporter.invalidMethodInvocation(
@@ -767,7 +803,12 @@
return right;
}
errorReporter.invalidBinaryOperandType(
- contextChain, node, 'bool', '${node.operator}', 'bool', '$right');
+ contextChain,
+ node,
+ left,
+ '${node.operator}',
+ typeEnvironment.boolType,
+ right.getType(typeEnvironment));
throw const _AbortCurrentEvaluation();
}
errorReporter.invalidMethodInvocation(
@@ -789,7 +830,8 @@
} else if (constant == falseConstant) {
return evaluate(node.otherwise);
} else {
- errorReporter.invalidType(contextChain, node, constant, 'bool');
+ errorReporter.invalidDartType(
+ contextChain, node, constant, typeEnvironment.boolType);
throw const _AbortCurrentEvaluation();
}
}
@@ -834,8 +876,8 @@
if (!variable.isConst &&
!_isFormalParameter(variable) &&
variable.parent is! Let) {
- errorReporter.nonConstantVariableGet(contextChain, node);
- throw const _AbortCurrentEvaluation();
+ throw new Exception('The front-end should ensure we do not encounter a '
+ 'variable get of a non-const variable.');
}
return env.lookupVariable(node.variable);
}
@@ -854,9 +896,8 @@
errorReporter.invalidStaticInvocation(contextChain, node, target);
throw const _AbortCurrentEvaluation();
} else {
- errorReporter.unreachable(contextChain, node,
+ throw new Exception(
'No support for ${target.runtimeType} in a static-get.');
- throw const _AbortCurrentEvaluation();
}
});
}
@@ -903,11 +944,7 @@
if (parent is Library && parent == coreTypes.coreLibrary) {
final positionalArguments = evaluatePositionalArguments(node.arguments);
final Constant left = positionalArguments[0];
- // TODO(http://dartbug.com/31799): Re-enable these checks.
- //ensurePrimitiveConstant(left, node);
final Constant right = positionalArguments[1];
- // TODO(http://dartbug.com/31799): Re-enable these checks.
- //ensurePrimitiveConstant(right, node);
// Since we canonicalize constants during the evaluation, we can use
// identical here.
assert(left == right);
@@ -929,7 +966,8 @@
if (constant is BoolConstant) {
return constant == trueConstant ? falseConstant : trueConstant;
}
- errorReporter.invalidType(contextChain, node, constant, 'bool');
+ errorReporter.invalidDartType(
+ contextChain, node, constant, typeEnvironment.boolType);
throw const _AbortCurrentEvaluation();
}
@@ -946,16 +984,12 @@
return canonicalize(
new PartialInstantiationConstant(constant, node.typeArguments));
}
- errorReporter.unreachable(
- contextChain,
- node,
+ throw new Exception(
'The number of type arguments supplied in the partial instantiation '
'does not match the number of type arguments of the $constant.');
- throw const _AbortCurrentEvaluation();
}
- errorReporter.unreachable(contextChain, node,
+ throw new Exception(
'Only tear-off constants can be partially instantiated.');
- throw const _AbortCurrentEvaluation();
}
// Helper methods:
@@ -985,13 +1019,11 @@
} else if (constant is TypeLiteralConstant) {
constantType = new InterfaceType(coreTypes.typeClass);
} else {
- errorReporter.unreachable(contextChain, node,
- 'No support for ${constant.runtimeType}.runtimeType');
- throw const _AbortCurrentEvaluation();
+ throw new Exception('No support for ${constant.runtimeType}.runtimeType');
}
if (!typeEnvironment.isSubtypeOf(constantType, type)) {
- errorReporter.invalidType(contextChain, node, constant, '$type');
+ errorReporter.invalidDartType(contextChain, node, constant, type);
throw const _AbortCurrentEvaluation();
}
}
@@ -1015,7 +1047,7 @@
List<Constant> evaluatePositionalArguments(Arguments arguments) {
return arguments.positional.map((Expression node) {
- return node.accept(this);
+ return node.accept(this) as Constant;
}).toList();
}
@@ -1053,18 +1085,6 @@
}
}
- ensurePrimitiveConstant(Constant value, TreeNode node) {
- if (value is! NullConstant &&
- value is! BoolConstant &&
- value is! IntConstant &&
- value is! DoubleConstant &&
- value is! StringConstant) {
- errorReporter.invalidType(
- contextChain, node, value, 'bool/int/double/Null/String');
- throw const _AbortCurrentEvaluation();
- }
- }
-
evaluateBinaryNumericOperation(String op, num a, num b, TreeNode node) {
num result;
switch (op) {
@@ -1105,8 +1125,7 @@
return a > b ? trueConstant : falseConstant;
}
- errorReporter.invalidBinaryMethodInvocation(contextChain, node, op);
- throw const _AbortCurrentEvaluation();
+ throw new Exception("Unexpected binary numeric operation '$op'.");
}
int _wrapAroundInteger(int value) {
@@ -1211,32 +1230,23 @@
abstract class ErrorReporter {
const ErrorReporter();
- invalidType(List<TreeNode> context, TreeNode node, Constant receiver,
- String expectedType);
+ invalidDartType(List<TreeNode> context, TreeNode node, Constant receiver,
+ DartType expectedType);
invalidBinaryOperandType(List<TreeNode> context, TreeNode node,
- String receiverType, String op, String expectedType, String actualType);
- invalidBinaryMethodInvocation(
- List<TreeNode> context, TreeNode node, String op);
+ Constant receiver, String op, DartType expectedType, DartType actualType);
invalidMethodInvocation(
List<TreeNode> context, TreeNode node, Constant receiver, String op);
invalidStaticInvocation(
List<TreeNode> context, TreeNode node, Procedure target);
invalidStringInterpolationOperand(
List<TreeNode> context, TreeNode node, Constant constant);
+ nonConstLiteral(List<TreeNode> context, TreeNode node, String klass);
duplicateKey(List<TreeNode> context, TreeNode node, Constant key);
- nonConstantLiteral(List<TreeNode> context, TreeNode node, String literalType);
- nonConstantVariableGet(List<TreeNode> context, VariableGet node);
- nonConstConstructorInvocation(
- List<TreeNode> context, TreeNode node, Constructor target);
- failedAssertion(List<TreeNode> context, TreeNode node, Constant message);
- unimplemented(List<TreeNode> context, TreeNode node, String message);
- unreachable(List<TreeNode> context, TreeNode node, String message);
+ failedAssertion(List<TreeNode> context, TreeNode node, String message);
}
-abstract class ErrorReporterBase implements ErrorReporter {
- const ErrorReporterBase();
-
- report(List<TreeNode> context, String message, TreeNode node);
+class _SimpleErrorReporter implements ErrorReporter {
+ const _SimpleErrorReporter();
getFileUri(TreeNode node) {
while (node is! FileUriNode) {
@@ -1252,28 +1262,28 @@
return node == null ? TreeNode.noOffset : node.fileOffset;
}
- invalidType(List<TreeNode> context, TreeNode node, Constant receiver,
- String expectedType) {
+ invalidDartType(List<TreeNode> context, TreeNode node, Constant receiver,
+ DartType expectedType) {
report(
context,
'Expected expression to evaluate to "$expectedType" but got "$receiver.',
node);
}
- invalidBinaryOperandType(List<TreeNode> context, TreeNode node,
- String receiverType, String op, String expectedType, String actualType) {
+ invalidBinaryOperandType(
+ List<TreeNode> context,
+ TreeNode node,
+ Constant receiver,
+ String op,
+ DartType expectedType,
+ DartType actualType) {
report(
context,
- 'Calling "$op" on "$receiverType" needs operand of type '
+ 'Calling "$op" on "$receiver" needs operand of type '
'"$expectedType" (but got "$actualType")',
node);
}
- invalidBinaryMethodInvocation(
- List<TreeNode> context, TreeNode node, String op) {
- report(context, 'Cannot call "$op" on a numeric constant receiver', node);
- }
-
invalidMethodInvocation(
List<TreeNode> context, TreeNode node, Constant receiver, String op) {
report(context, 'Cannot call "$op" on "$receiver" in constant expression',
@@ -1295,6 +1305,13 @@
node);
}
+ nonConstLiteral(List<TreeNode> context, TreeNode node, String klass) {
+ report(
+ context,
+ 'Cannot have a non-constant $klass literal within a const context.',
+ node);
+ }
+
duplicateKey(List<TreeNode> context, TreeNode node, Constant key) {
report(
context,
@@ -1302,52 +1319,13 @@
node);
}
- nonConstantLiteral(
- List<TreeNode> context, TreeNode node, String literalType) {
- report(
- context,
- '"$literalType" literals inside constant expressions are required to '
- 'be constant.',
- node);
- }
-
- nonConstantVariableGet(List<TreeNode> context, VariableGet node) {
- report(
- context,
- 'The variable "${node.variable.name}" is non-const and '
- 'can therefore not be used inside a constant expression.'
- ' (${node}, ${node.parent.parent}, )',
- node);
- }
-
- nonConstConstructorInvocation(
- List<TreeNode> context, TreeNode node, Constructor target) {
- report(
- context,
- 'The non-const constructor "$target" cannot be called inside a '
- 'constant expression',
- node);
- }
-
- failedAssertion(List<TreeNode> context, TreeNode node, Constant message) {
+ failedAssertion(List<TreeNode> context, TreeNode node, String message) {
report(
context,
'The assertion condition evaluated to "false" with message "$message"',
node);
}
- unimplemented(List<TreeNode> context, TreeNode node, String message) {
- report(context, 'Unimplemented: $message', node);
- }
-
- unreachable(List<TreeNode> context, TreeNode node, String message) {
- report(context, 'Unreachable: $message', node);
- }
-}
-
-class _SimpleErrorReporter extends ErrorReporterBase {
- const _SimpleErrorReporter();
-
report(List<TreeNode> context, String message, TreeNode node) {
io.exitCode = 42;
final Uri uri = getFileUri(node);
diff --git a/pkg/vm/bin/gen_kernel.dart b/pkg/vm/bin/gen_kernel.dart
index 0a2e4a2..8888c35 100644
--- a/pkg/vm/bin/gen_kernel.dart
+++ b/pkg/vm/bin/gen_kernel.dart
@@ -8,14 +8,14 @@
import 'package:args/args.dart' show ArgParser, ArgResults;
import 'package:front_end/src/api_prototype/front_end.dart';
import 'package:kernel/binary/ast_to_binary.dart';
-import 'package:kernel/kernel.dart' show Component;
import 'package:kernel/src/tool/batch_util.dart' as batch_util;
import 'package:kernel/target/targets.dart' show TargetFlags;
import 'package:kernel/target/vm.dart' show VmTarget;
import 'package:kernel/text/ast_to_text.dart'
show globalDebuggingNames, NameSystem;
import 'package:vm/bytecode/gen_bytecode.dart' show isKernelBytecodeEnabled;
-import 'package:vm/kernel_front_end.dart' show compileToKernel, ErrorDetector;
+import 'package:vm/kernel_front_end.dart'
+ show compileToKernel, ErrorDetector, ErrorPrinter;
final ArgParser _argParser = new ArgParser(allowTrailingOptions: true)
..addOption('platform',
@@ -36,6 +36,14 @@
help:
'Enable global type flow analysis and related transformations in AOT mode.',
defaultsTo: true)
+ ..addMultiOption('define',
+ abbr: 'D',
+ help: 'The values for the environment constants (e.g. -Dkey=value).')
+ ..addFlag('enable-asserts',
+ help: 'Whether asserts will be enabled.', defaultsTo: false)
+ ..addFlag('enable-constant-evaluation',
+ help: 'Whether kernel constant evaluation will be enabled.',
+ defaultsTo: true)
..addMultiOption('entry-points',
help: 'Path to JSON file with the list of entry points')
..addFlag('gen-bytecode',
@@ -77,6 +85,13 @@
final bool syncAsync = options['sync-async'];
final bool tfa = options['tfa'];
final bool genBytecode = options['gen-bytecode'];
+ final bool enableAsserts = options['enable-asserts'];
+ final bool enableConstantEvaluation = options['enable-constant-evaluation'];
+ final Map<String, String> environmentDefines = {};
+
+ if (!_parseDefines(options['define'], environmentDefines)) {
+ return _badUsageExitCode;
+ }
final List<String> entryPoints = options['entry-points'] ?? <String>[];
if (entryPoints.isEmpty) {
@@ -87,7 +102,8 @@
]);
}
- ErrorDetector errorDetector = new ErrorDetector();
+ final errorPrinter = new ErrorPrinter();
+ final errorDetector = new ErrorDetector(previousErrorHandler: errorPrinter);
final CompilerOptions compilerOptions = new CompilerOptions()
..strongMode = strongMode
@@ -99,15 +115,21 @@
..packagesFileUri =
packages != null ? Uri.base.resolveUri(new Uri.file(packages)) : null
..reportMessages = true
- ..onError = errorDetector
+ ..onProblem = errorDetector
..embedSourceText = options['embed-sources'];
- Component component = await compileToKernel(
- Uri.base.resolveUri(new Uri.file(filename)), compilerOptions,
+ final inputUri = new Uri.file(filename);
+ final component = await compileToKernel(
+ Uri.base.resolveUri(inputUri), compilerOptions,
aot: aot,
useGlobalTypeFlowAnalysis: tfa,
entryPoints: entryPoints,
- genBytecode: genBytecode);
+ environmentDefines: environmentDefines,
+ genBytecode: genBytecode,
+ enableAsserts: enableAsserts,
+ enableConstantEvaluation: enableConstantEvaluation);
+
+ errorPrinter.printCompilationMessages(inputUri);
if (errorDetector.hasCompilationErrors || (component == null)) {
return _compileTimeErrorExitCode;
@@ -152,3 +174,22 @@
}
});
}
+
+bool _parseDefines(
+ List<String> dFlags, Map<String, String> environmentDefines) {
+ for (final String dflag in dFlags) {
+ final equalsSignIndex = dflag.indexOf('=');
+ if (equalsSignIndex < 0) {
+ environmentDefines[dflag] = '';
+ } else if (equalsSignIndex > 0) {
+ final key = dflag.substring(0, equalsSignIndex);
+ final value = dflag.substring(equalsSignIndex + 1);
+ environmentDefines[key] = value;
+ } else {
+ print('The environment constant options must have a key (was: "$dflag")');
+ print(_usage);
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 8bf56e0..04521b1 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -7,16 +7,34 @@
import 'dart:async';
+import 'package:front_end/src/base/processed_options.dart'
+ show ProcessedOptions;
+import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
+import 'package:front_end/src/fasta/fasta_codes.dart' as codes;
+
import 'package:front_end/src/api_prototype/compiler_options.dart'
- show CompilerOptions, ErrorHandler;
+ show CompilerOptions, ProblemHandler;
import 'package:front_end/src/api_prototype/kernel_generator.dart'
show kernelForProgram;
import 'package:front_end/src/api_prototype/compilation_message.dart'
- show CompilationMessage, Severity;
-import 'package:front_end/src/fasta/severity.dart' show Severity;
-import 'package:kernel/ast.dart' show Component, StaticGet, Field;
+ show Severity;
+import 'package:kernel/type_environment.dart' show TypeEnvironment;
+import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
+import 'package:kernel/ast.dart'
+ show
+ Component,
+ Constant,
+ DartType,
+ Field,
+ FileUriNode,
+ Procedure,
+ StaticGet,
+ TreeNode;
import 'package:kernel/core_types.dart' show CoreTypes;
-import 'package:vm/bytecode/gen_bytecode.dart' show generateBytecode;
+import 'package:kernel/transformations/constants.dart' as constants;
+import 'package:kernel/vm/constants_native_effects.dart' as vm_constants;
+
+import 'bytecode/gen_bytecode.dart' show generateBytecode;
import 'transformations/devirtualization.dart' as devirtualization
show transformComponent;
@@ -36,23 +54,35 @@
{bool aot: false,
bool useGlobalTypeFlowAnalysis: false,
List<String> entryPoints,
- bool genBytecode: false}) async {
+ Map<String, String> environmentDefines,
+ bool genBytecode: false,
+ bool enableAsserts: false,
+ bool enableConstantEvaluation: true}) async {
// Replace error handler to detect if there are compilation errors.
final errorDetector =
- new ErrorDetector(previousErrorHandler: options.onError);
- options.onError = errorDetector;
+ new ErrorDetector(previousErrorHandler: options.onProblem);
+ options.onProblem = errorDetector;
final component = await kernelForProgram(source, options);
- // Restore error handler (in case 'options' are reused).
- options.onError = errorDetector.previousErrorHandler;
-
// Run global transformations only if component is correct.
- if (aot && (component != null) && !errorDetector.hasCompilationErrors) {
- _runGlobalTransformations(
- component, options.strongMode, useGlobalTypeFlowAnalysis, entryPoints);
+ if (aot && component != null) {
+ await _runGlobalTransformations(
+ source,
+ options,
+ component,
+ options.strongMode,
+ useGlobalTypeFlowAnalysis,
+ entryPoints,
+ environmentDefines,
+ enableAsserts,
+ enableConstantEvaluation,
+ errorDetector);
}
+ // Restore error handler (in case 'options' are reused).
+ options.onProblem = errorDetector.previousErrorHandler;
+
if (genBytecode && component != null) {
generateBytecode(component, strongMode: options.strongMode);
}
@@ -60,11 +90,21 @@
return component;
}
-_runGlobalTransformations(Component component, bool strongMode,
- bool useGlobalTypeFlowAnalysis, List<String> entryPoints) {
+Future _runGlobalTransformations(
+ Uri source,
+ CompilerOptions compilerOptions,
+ Component component,
+ bool strongMode,
+ bool useGlobalTypeFlowAnalysis,
+ List<String> entryPoints,
+ Map<String, String> environmentDefines,
+ bool enableAsserts,
+ bool enableConstantEvaluation,
+ ErrorDetector errorDetector) async {
if (strongMode) {
- final coreTypes = new CoreTypes(component);
+ if (errorDetector.hasCompilationErrors) return;
+ final coreTypes = new CoreTypes(component);
_patchVmConstants(coreTypes);
// TODO(alexmarkov, dmitryas): Consider doing canonicalization of identical
@@ -81,10 +121,56 @@
devirtualization.transformComponent(coreTypes, component);
}
+ if (enableConstantEvaluation) {
+ await _performConstantEvaluation(source, compilerOptions, component,
+ coreTypes, environmentDefines, strongMode, enableAsserts);
+
+ if (errorDetector.hasCompilationErrors) return;
+ }
+
no_dynamic_invocations_annotator.transformComponent(component);
}
}
+Future _performConstantEvaluation(
+ Uri source,
+ CompilerOptions compilerOptions,
+ Component component,
+ CoreTypes coreTypes,
+ Map<String, String> environmentDefines,
+ bool strongMode,
+ bool enableAsserts) async {
+ final vmConstants =
+ new vm_constants.VmConstantsBackend(environmentDefines, coreTypes);
+
+ final processedOptions =
+ new ProcessedOptions(compilerOptions, false, [source]);
+
+ // Run within the context, so we have uri source tokens...
+ await CompilerContext.runWithOptions(processedOptions,
+ (CompilerContext context) async {
+ // To make the fileUri/fileOffset -> line/column mapping, we need to
+ // pre-fill the map.
+ context.uriToSource.addAll(component.uriToSource);
+
+ final hierarchy = new ClassHierarchy(component);
+ final typeEnvironment =
+ new TypeEnvironment(coreTypes, hierarchy, strongMode: strongMode);
+
+ // NOTE: Currently we keep fields, because there are certain constant
+ // fields which the VM accesses (e.g. `_Random._A` needs to be preserved).
+ // TODO(kustermann): We should use the entrypoints manifest to find out
+ // which fields need to be preserved and remove the rest.
+ constants.transformComponent(component, vmConstants,
+ keepFields: true,
+ strongMode: true,
+ evaluateAnnotations: false,
+ enableAsserts: enableAsserts,
+ errorReporter:
+ new ForwardConstantEvaluationErrors(context, typeEnvironment));
+ });
+}
+
void _patchVmConstants(CoreTypes coreTypes) {
// Fix Endian.host to be a const field equal to Endial.little instead of
// a final field. VM does not support big-endian architectures at the
@@ -101,16 +187,147 @@
}
class ErrorDetector {
- final ErrorHandler previousErrorHandler;
+ final ProblemHandler previousErrorHandler;
bool hasCompilationErrors = false;
ErrorDetector({this.previousErrorHandler});
- void call(CompilationMessage message) {
- if (message.severity == Severity.error) {
+ void call(codes.FormattedMessage problem, Severity severity,
+ List<codes.FormattedMessage> context) {
+ if (severity == Severity.error) {
hasCompilationErrors = true;
}
- previousErrorHandler?.call(message);
+ previousErrorHandler?.call(problem, severity, context);
+ }
+}
+
+class ErrorPrinter {
+ final ProblemHandler previousErrorHandler;
+ final compilationMessages = <Uri, List<List>>{};
+
+ ErrorPrinter({this.previousErrorHandler});
+
+ void call(codes.FormattedMessage problem, Severity severity,
+ List<codes.FormattedMessage> context) {
+ final sourceUri = problem.locatedMessage.uri;
+ compilationMessages.putIfAbsent(sourceUri, () => [])
+ ..add([problem, context]);
+ previousErrorHandler?.call(problem, severity, context);
+ }
+
+ void printCompilationMessages(Uri baseUri) {
+ final sortedUris = compilationMessages.keys.toList()
+ ..sort((a, b) => '$a'.compareTo('$b'));
+ for (final Uri sourceUri in sortedUris) {
+ for (final List errorTuple in compilationMessages[sourceUri]) {
+ final codes.FormattedMessage message = errorTuple.first;
+ print(message.formatted);
+
+ final List context = errorTuple.last;
+ for (final codes.FormattedMessage message in context?.reversed) {
+ print(message.formatted);
+ }
+ }
+ }
+ }
+}
+
+class ForwardConstantEvaluationErrors implements constants.ErrorReporter {
+ final CompilerContext compilerContext;
+ final TypeEnvironment typeEnvironment;
+
+ ForwardConstantEvaluationErrors(this.compilerContext, this.typeEnvironment);
+
+ duplicateKey(List<TreeNode> context, TreeNode node, Constant key) {
+ final message = codes.templateConstEvalDuplicateKey.withArguments(key);
+ reportIt(context, message, node);
+ }
+
+ invalidDartType(List<TreeNode> context, TreeNode node, Constant receiver,
+ DartType expectedType) {
+ final message = codes.templateConstEvalInvalidType.withArguments(
+ receiver, expectedType, receiver.getType(typeEnvironment));
+ reportIt(context, message, node);
+ }
+
+ invalidBinaryOperandType(
+ List<TreeNode> context,
+ TreeNode node,
+ Constant receiver,
+ String op,
+ DartType expectedType,
+ DartType actualType) {
+ final message = codes.templateConstEvalInvalidBinaryOperandType
+ .withArguments(op, receiver, expectedType, actualType);
+ reportIt(context, message, node);
+ }
+
+ invalidMethodInvocation(
+ List<TreeNode> context, TreeNode node, Constant receiver, String op) {
+ final message = codes.templateConstEvalInvalidMethodInvocation
+ .withArguments(op, receiver);
+ reportIt(context, message, node);
+ }
+
+ invalidStaticInvocation(
+ List<TreeNode> context, TreeNode node, Procedure target) {
+ final message = codes.templateConstEvalInvalidStaticInvocation
+ .withArguments(target.name.toString());
+ reportIt(context, message, node);
+ }
+
+ invalidStringInterpolationOperand(
+ List<TreeNode> context, TreeNode node, Constant constant) {
+ final message = codes.templateConstEvalInvalidStringInterpolationOperand
+ .withArguments(constant);
+ reportIt(context, message, node);
+ }
+
+ nonConstLiteral(List<TreeNode> context, TreeNode node, String klass) {
+ final message =
+ codes.templateConstEvalNonConstantLiteral.withArguments(klass);
+ reportIt(context, message, node);
+ }
+
+ failedAssertion(List<TreeNode> context, TreeNode node, String string) {
+ final message = string == null
+ ? codes.messageConstEvalFailedAssertion
+ : codes.templateConstEvalFailedAssertionWithMessage
+ .withArguments(string);
+ reportIt(context, message, node);
+ }
+
+ reportIt(List<TreeNode> context, codes.Message message, TreeNode node) {
+ final Uri uri = getFileUri(node);
+ final int fileOffset = getFileOffset(node);
+
+ final contextMessages = <codes.LocatedMessage>[];
+ for (final TreeNode node in context) {
+ final Uri uri = getFileUri(node);
+ final int fileOffset = getFileOffset(node);
+ contextMessages.add(codes.messageConstEvalContext
+ .withLocation(uri, fileOffset, codes.noLength));
+ }
+
+ final locatedMessage =
+ message.withLocation(uri, fileOffset, codes.noLength);
+
+ compilerContext.options
+ .report(locatedMessage, Severity.error, context: contextMessages);
+ }
+
+ getFileUri(TreeNode node) {
+ while (node is! FileUriNode) {
+ node = node.parent;
+ }
+ return (node as FileUriNode).fileUri;
+ }
+
+ getFileOffset(TreeNode node) {
+ while (node.fileOffset == TreeNode.noOffset) {
+ node = node.parent;
+ }
+ return node == null ? TreeNode.noOffset : node.fileOffset;
}
}
diff --git a/pkg/vm/tool/precompiler2 b/pkg/vm/tool/precompiler2
index cd965fe..64908b5 100755
--- a/pkg/vm/tool/precompiler2
+++ b/pkg/vm/tool/precompiler2
@@ -24,10 +24,15 @@
--packages=*)
PACKAGES="$arg"
;;
+ --enable-asserts)
+ GEN_KERNEL_OPTIONS+=("$arg")
+ OPTIONS+=("$arg")
+ ;;
--sync-async | \
--no-sync-async | \
--tfa | \
- --no-tfa )
+ --no-tfa | \
+ -D* )
GEN_KERNEL_OPTIONS+=("$arg")
;;
--*)
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index a3b0e33..2d94185 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -2712,6 +2712,7 @@
CollectImmediateSuperInterfaces(cls, &cids);
RemoveCHAOptimizedCode(cls, cids);
}
+
if (cls.is_enum_class()) {
AllocateEnumValues(cls);
}
@@ -2765,11 +2766,15 @@
(sentinel.raw() == field.raw())) {
continue;
}
- field.SetStaticValue(Object::transition_sentinel());
- result = Compiler::EvaluateStaticInitializer(field);
- ASSERT(!result.IsError());
- field.SetStaticValue(Instance::Cast(result), true);
- field.RecordStore(Instance::Cast(result));
+ // The eager evaluation of the enum values is required for hot-reload (see
+ // commit e3ecc87).
+ if (!FLAG_precompiled_mode) {
+ field.SetStaticValue(Object::transition_sentinel());
+ result = Compiler::EvaluateStaticInitializer(field);
+ ASSERT(!result.IsError());
+ field.SetStaticValue(Instance::Cast(result), true);
+ field.RecordStore(Instance::Cast(result));
+ }
}
} else {
const String& name_prefix =
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index 6dc969f..2d3376a 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -68,11 +68,8 @@
call_non_method_field_test/01: MissingCompileTimeError
call_non_method_field_test/02: MissingCompileTimeError
check_member_static_test/01: MissingCompileTimeError
-compile_time_constant_o_test/01: MissingCompileTimeError
-compile_time_constant_o_test/02: MissingCompileTimeError
const_cast2_test/01: CompileTimeError
const_cast2_test/none: CompileTimeError
-const_dynamic_type_literal_test/02: MissingCompileTimeError
const_instance_field_test/01: MissingCompileTimeError # Fasta bug: Const instance field. Issue 32326.
const_map2_test/00: MissingCompileTimeError # KernelVM bug: Constant evaluation.
const_map3_test/00: MissingCompileTimeError # KernelVM bug: Constant evaluation.
@@ -104,10 +101,6 @@
generic_methods_overriding_test/01: MissingCompileTimeError
generic_methods_recursive_bound_test/02: MissingCompileTimeError
getter_override_test/03: MissingCompileTimeError
-identical_const_test/01: MissingCompileTimeError
-identical_const_test/02: MissingCompileTimeError
-identical_const_test/03: MissingCompileTimeError
-identical_const_test/04: MissingCompileTimeError
issue31596_override_test/07: MissingCompileTimeError
issue31596_override_test/08: MissingCompileTimeError
issue31596_super_test/02: MissingCompileTimeError
@@ -448,10 +441,15 @@
[ $compiler != dart2js && $compiler != dartk && $compiler != dartkp && $fasta ]
const_optional_args_test/01: MissingCompileTimeError
-[ $compiler != dart2js && $fasta ]
+# The precomilation configuration uses a kernel2kernel constants evaluator
+# which is is more correct than fasta/vm in JIT mode (i.e. it catches more
+# compile-time errors).
+[ $compiler != dart2js && $compiler != dartkp && $fasta ]
compile_time_constant_c_test/02: MissingCompileTimeError
const_constructor_nonconst_field_test/01: MissingCompileTimeError
const_syntax_test/05: MissingCompileTimeError
+
+[ $compiler != dart2js && $fasta ]
mixin_super_2_test/01: MissingCompileTimeError
mixin_super_2_test/03: MissingCompileTimeError
mixin_supertype_subclass_test/02: MissingCompileTimeError
@@ -857,11 +855,6 @@
checked_setter3_test/01: MissingCompileTimeError
checked_setter3_test/02: MissingCompileTimeError
checked_setter3_test/03: MissingCompileTimeError
-compile_time_constant_k_test/01: MissingCompileTimeError
-compile_time_constant_k_test/02: MissingCompileTimeError
-compile_time_constant_k_test/03: MissingCompileTimeError
-compile_time_constant_o_test/01: RuntimeError # KernelVM bug: Constant map duplicated key.
-compile_time_constant_o_test/02: RuntimeError # KernelVM bug: Constant map duplicated key.
compile_time_constant_static5_test/11: CompileTimeError # Issue 31537
compile_time_constant_static5_test/16: CompileTimeError # Issue 31537
compile_time_constant_static5_test/21: CompileTimeError # Issue 31537
@@ -870,11 +863,9 @@
conditional_import_test: CompileTimeError # KernelVM bug: Deferred loading kernel issue 30273.
config_import_corelib_test: CompileTimeError # Issue 31533
config_import_test: RuntimeError # KernelVM bug: Configurable imports.
-const_dynamic_type_literal_test/02: RuntimeError # KernelVM bug: Constant map duplicated key.
const_evaluation_test: SkipByDesign
const_list_test: RuntimeError
const_map4_test: RuntimeError
-const_nested_test: RuntimeError # KernelVM bug: Constant evaluation.
constructor12_test: RuntimeError
constructor3_test: Fail, OK, Pass
ct_const2_test: Skip # Incompatible flag: --compile_all
@@ -974,8 +965,6 @@
main_not_a_function_test: Skip
main_test/03: RuntimeError
many_overridden_no_such_method_test: SkipByDesign
-map_literal3_test/01: MissingCompileTimeError
-map_literal3_test/02: MissingCompileTimeError
map_literal3_test/03: MissingCompileTimeError
method_override4_test/01: MissingCompileTimeError
method_override4_test/02: MissingCompileTimeError
@@ -1103,6 +1092,28 @@
tearoff_dynamic_test: RuntimeError # Compares call to "foo" (noSuchMethod) with string "foo"
type_variable_promotion_test: RuntimeError # Compares runtime type to the string "List<B>"
+# The precomilation configuration uses a kernel2kernel constants evaluator
+# which is is more correct than fasta/vm in JIT mode (i.e. it catches more
+# compile-time errors).
+[ $compiler != dartkp && $fasta ]
+compile_time_constant_o_test/01: MissingCompileTimeError
+compile_time_constant_o_test/02: MissingCompileTimeError
+const_dynamic_type_literal_test/02: MissingCompileTimeError
+identical_const_test/01: MissingCompileTimeError
+identical_const_test/02: MissingCompileTimeError
+identical_const_test/03: MissingCompileTimeError
+identical_const_test/04: MissingCompileTimeError
+
+# The precomilation configuration uses a kernel2kernel constants evaluator
+# which is is more correct than fasta/vm in JIT mode (i.e. it catches more
+# compile-time errors).
+[ $compiler != dartkp && $fasta && $strong ]
+compile_time_constant_k_test/01: MissingCompileTimeError
+compile_time_constant_k_test/02: MissingCompileTimeError
+compile_time_constant_k_test/03: MissingCompileTimeError
+map_literal3_test/01: MissingCompileTimeError
+map_literal3_test/02: MissingCompileTimeError
+
[ $compiler == fasta && $strong ]
bad_override_test/03: MissingCompileTimeError
check_member_static_test/02: MissingCompileTimeError
@@ -1296,16 +1307,11 @@
variable_shadow_class_test/01: MissingCompileTimeError
[ $fasta && $strong ]
-compile_time_constant_k_test/01: MissingCompileTimeError
-compile_time_constant_k_test/02: MissingCompileTimeError
-compile_time_constant_k_test/03: MissingCompileTimeError
compile_time_constant_static2_test/04: MissingCompileTimeError
compile_time_constant_static3_test/04: MissingCompileTimeError
initializing_formal_type_annotation_test/01: MissingCompileTimeError
initializing_formal_type_annotation_test/02: MissingCompileTimeError
issue18628_2_test/01: MissingCompileTimeError
-map_literal3_test/01: MissingCompileTimeError
-map_literal3_test/02: MissingCompileTimeError
map_literal3_test/03: MissingCompileTimeError
redirecting_factory_infinite_steps_test/01: MissingCompileTimeError
redirecting_factory_malbounded_test/01: MissingCompileTimeError
diff --git a/tools/testing/dart/compiler_configuration.dart b/tools/testing/dart/compiler_configuration.dart
index 4bc4061..37df80f 100644
--- a/tools/testing/dart/compiler_configuration.dart
+++ b/tools/testing/dart/compiler_configuration.dart
@@ -1062,6 +1062,8 @@
bool get _useSdk;
bool get _isStrong;
bool get _isAot;
+ bool get _isChecked;
+ bool get _useEnableAsserts;
String get executableScriptSuffix;
@@ -1107,6 +1109,10 @@
}
args.add(arguments.where((name) => name.endsWith('.dart')).single);
+ args.addAll(arguments.where((name) => name.startsWith('-D')));
+ if (_isChecked || _useEnableAsserts) {
+ args.add('--enable_asserts');
+ }
// Pass environment variable to the gen_kernel script as
// arguments are not passed if gen_kernel runs in batch mode.