Index: pkg/compiler/lib/src/resolution/members.dart |
diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart |
index 90f536553150628ae9a9f676416195b3666297a0..cda8e96a42b70df8ef37f2d47dabab98534ddcda 100644 |
--- a/pkg/compiler/lib/src/resolution/members.dart |
+++ b/pkg/compiler/lib/src/resolution/members.dart |
@@ -10,7 +10,11 @@ import '../common/names.dart' show |
import '../compiler.dart' show |
Compiler; |
import '../constants/constructors.dart' show |
+ ErroneousConstantConstructor, |
RedirectingFactoryConstantConstructor; |
+import '../constants/evaluation.dart' show |
+ ConstantTypeChecker, |
+ ConstantTypeInfo; |
import '../constants/expressions.dart'; |
import '../constants/values.dart'; |
import '../core_types.dart'; |
@@ -67,21 +71,82 @@ import 'variables.dart' show |
VariableDefinitionsVisitor; |
/// The state of constants in resolutions. |
-enum ConstantState { |
- /// Expressions are not required to be constants. |
- NON_CONSTANT, |
+abstract class ConstantState { |
- /// Expressions are required to be constants. |
+ /// `true` if expressions are required to be constant. |
+ bool get requiresConstant; |
+ |
+ /// `true` if parameter references are considered constant. This is the case |
+ /// in const constructor initializers. |
+ bool get parameterReferenceIsConstant; |
+ |
+ /// Returns the constant state corresponding to this state but where |
+ /// expression are required to be constants. |
+ ConstantState get requireConstant; |
+ |
+ /// Constant state where expressions are not required to be constants. |
+ static const ConstantState NON_CONSTANT = const _NoneConstantState(); |
+ |
+ /// Constant state where expressions are required to be constants. |
/// |
/// For instance the values of a constant list literal. |
- CONSTANT, |
+ static const ConstantState CONSTANT = const _RequiredConstantState(); |
- /// Expressions are required to be constants and parameter references are |
- /// also considered constant. |
+ /// Constant state where expressions are required to be constants and |
+ /// parameter references are also considered constant. |
/// |
/// This is used for resolving constructor initializers of constant |
/// constructors. |
- CONSTANT_INITIALIZER, |
+ static const ConstantState CONSTANT_INITIALIZER = |
+ const _ConstantInitializerState(); |
+} |
+ |
+/// Constant state where expressions are not required to be constants. |
+class _NoneConstantState implements ConstantState { |
+ const _NoneConstantState(); |
+ |
+ @override |
+ bool get requiresConstant => false; |
+ |
+ @override |
+ bool get parameterReferenceIsConstant => false; |
+ |
+ @override |
+ ConstantState get requireConstant => ConstantState.CONSTANT; |
+} |
+ |
+/// Constant state where expressions are required to be constants. |
+/// |
+/// For instance the values of a constant list literal. |
+class _RequiredConstantState implements ConstantState { |
+ const _RequiredConstantState(); |
+ |
+ @override |
+ bool get requiresConstant => true; |
+ |
+ @override |
+ bool get parameterReferenceIsConstant => false; |
+ |
+ @override |
+ ConstantState get requireConstant => this; |
+} |
+ |
+/// Constant state where expressions are required to be constants and parameter |
+/// references are also considered constant. |
+/// |
+/// This is used for resolving constructor initializers of constant |
+/// constructors. |
+class _ConstantInitializerState implements ConstantState { |
+ const _ConstantInitializerState(); |
+ |
+ @override |
+ bool get requiresConstant => true; |
+ |
+ @override |
+ bool get parameterReferenceIsConstant => true; |
+ |
+ @override |
+ ConstantState get requireConstant => this; |
} |
/** |
@@ -104,6 +169,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
bool inCheckContext; |
bool inCatchBlock; |
ConstantState constantState; |
+ final ConstantNodeChecker constantExpressionChecker; |
+ final ConstantNodeChecker constantExpressionValidator; |
Scope scope; |
ClassElement currentClass; |
@@ -182,10 +249,22 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
inCatchBlock = false, |
constantState = element.isConst |
? ConstantState.CONSTANT : ConstantState.NON_CONSTANT, |
+ constantExpressionChecker = new ConstantNodeChecker( |
+ compiler, constantIsRequired: true), |
+ constantExpressionValidator = new ConstantNodeChecker( |
+ compiler, constantIsRequired: false), |
super(compiler, registry); |
CoreTypes get coreTypes => compiler.coreTypes; |
+ ConstantNodeChecker get constantChecker { |
+ if (constantState.requiresConstant) { |
+ return constantExpressionChecker; |
+ } else { |
+ return constantExpressionValidator; |
+ } |
+ } |
+ |
AsyncMarker get currentAsyncMarker { |
if (enclosingElement is FunctionElement) { |
FunctionElement function = enclosingElement; |
@@ -264,18 +343,33 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
inConstantInitializer: inConstantInitializer); |
} |
+ /// Execute [action] where the constant state is `ConstantState.NON_CONSTANT`. |
+ inNonConstantContext(action()) { |
+ ConstantState oldConstantState = constantState; |
+ constantState = ConstantState.NON_CONSTANT; |
+ var result = action(); |
+ constantState = oldConstantState; |
+ return result; |
+ } |
+ |
/// Execute [action] where the constant state is `ConstantState.CONSTANT` if |
/// not already `ConstantState.CONSTANT_INITIALIZER`. |
inConstantContext(action()) { |
ConstantState oldConstantState = constantState; |
- if (constantState != ConstantState.CONSTANT_INITIALIZER) { |
- constantState = ConstantState.CONSTANT; |
- } |
+ constantState = constantState.requireConstant; |
var result = action(); |
constantState = oldConstantState; |
return result; |
} |
+ bool checkConstantInvariant(Node node, ResolutionResult result) { |
+ return invariant( |
+ node, |
+ !constantState.requiresConstant || result.isConstant, |
+ message: "No constant computed for expression."); |
+ } |
+ |
+ |
/// Visit [node] where the constant state is `ConstantState.CONSTANT` if |
/// not already `ConstantState.CONSTANT_INITIALIZER`. |
ResolutionResult visitInConstantContext(Node node) { |
@@ -463,9 +557,9 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
if (element.isOptional) { |
if (element.initializer != null) { |
ResolutionResult result = visitInConstantContext(element.initializer); |
- if (result.isConstant) { |
- element.constant = result.constant; |
- } |
+ assert(invariant(element, result.isConstant, |
+ message: "No default value computed for $element.")); |
+ element.constant = result.constant; |
} else { |
element.constant = new NullConstantExpression(); |
} |
@@ -632,7 +726,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
// Run the body in a fresh statement scope. |
StatementScope oldStatementScope = statementScope; |
statementScope = new StatementScope(); |
- visit(node.body); |
+ inNonConstantContext(() => visit(node.body)); |
statementScope = oldStatementScope; |
scope = oldScope; |
@@ -640,7 +734,17 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
registry.registerClosure(function); |
registry.registerInstantiatedClass(compiler.functionClass); |
- return const NoneResult(); |
+ return ensureConstantResult(node, const NoneResult()); |
+ } |
+ |
+ ResolutionResult ensureConstantResult(Node node, ResolutionResult result) { |
+ if (constantState.requiresConstant && !result.isConstant) { |
+ reporter.reportErrorMessage( |
+ node, MessageKind.NOT_A_COMPILE_TIME_CONSTANT); |
+ return new ConstantResult( |
+ node, new ErroneousConstantExpression(), element: result.element); |
+ } |
+ return result; |
} |
ResolutionResult visitIf(If node) { |
@@ -741,6 +845,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
for (Link<Node> link = list.nodes; !link.isEmpty; link = link.tail) { |
Expression argument = link.head; |
ResolutionResult result = visit(argument); |
+ assert(checkConstantInvariant(argument, result)); |
if (!result.isConstant) { |
isValidAsConstant = false; |
} |
@@ -1227,37 +1332,29 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
} |
} else { |
ResolutionResult expressionResult = visitExpression(expression); |
+ assert(checkConstantInvariant(expression, expressionResult)); |
semantics = const DynamicAccess.expression(); |
registry.registerDynamicInvocation(new UniverseSelector(selector, null)); |
+ ConstantExpression constant; |
if (expressionResult.isConstant) { |
- bool isValidConstant; |
- ConstantExpression expressionConstant = expressionResult.constant; |
- DartType knownExpressionType = |
- expressionConstant.getKnownType(coreTypes); |
- switch (operator.kind) { |
- case UnaryOperatorKind.COMPLEMENT: |
- isValidConstant = |
- knownExpressionType == coreTypes.intType; |
- break; |
- case UnaryOperatorKind.NEGATE: |
- isValidConstant = |
- knownExpressionType == coreTypes.intType || |
- knownExpressionType == coreTypes.doubleType; |
- break; |
- case UnaryOperatorKind.NOT: |
- reporter.internalError(node, |
- "Unexpected user definable unary operator: $operator"); |
- } |
- if (isValidConstant) { |
- // TODO(johnniwinther): Handle potentially invalid constant |
- // expressions. |
- ConstantExpression constant = |
- new UnaryConstantExpression(operator, expressionConstant); |
- registry.setConstant(node, constant); |
- result = new ConstantResult(node, constant); |
+ bool isValidAsConstant = |
+ !expressionResult.constant.isErroneous && |
+ constantChecker.checkUnary( |
+ operator, |
+ constantChecker.createInfo(expressionResult)); |
+ if (isValidAsConstant) { |
+ constant = |
+ new UnaryConstantExpression(operator, expressionResult.constant); |
} |
} |
+ if (constant == null && constantState.requiresConstant) { |
+ constant = new ErroneousConstantExpression(); |
+ } |
+ if (constant != null) { |
+ registry.setConstant(node, constant); |
+ result = new ConstantResult(node, constant); |
+ } |
} |
if (semantics != null) { |
registry.registerSendStructure(node, |
@@ -1271,20 +1368,26 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
assert(invariant(node, operator.kind == UnaryOperatorKind.NOT)); |
Node expression = node.receiver; |
- ResolutionResult result = visitExpression(expression); |
+ ResolutionResult expressionResult = visitExpression(expression); |
registry.registerSendStructure(node, const NotStructure()); |
- if (result.isConstant) { |
- ConstantExpression expressionConstant = result.constant; |
- if (expressionConstant.getKnownType(coreTypes) == coreTypes.boolType) { |
- // TODO(johnniwinther): Handle potentially invalid constant expressions. |
- ConstantExpression constant = |
- new UnaryConstantExpression(operator, expressionConstant); |
- registry.setConstant(node, constant); |
- return new ConstantResult(node, constant); |
+ ConstantExpression constant; |
+ if (expressionResult.isConstant) { |
+ bool isValidAsConstant = constantChecker.checkUnary( |
+ operator, |
+ constantChecker.createInfo(expressionResult)); |
+ if (isValidAsConstant) { |
+ constant = |
+ new UnaryConstantExpression(operator, expressionResult.constant); |
} |
} |
- |
+ if (constant == null && constantState.requiresConstant) { |
+ constant = new ErroneousConstantExpression(); |
+ } |
+ if (constant != null) { |
+ registry.setConstant(node, constant); |
+ return new ConstantResult(node, constant); |
+ } |
return const NoneResult(); |
} |
@@ -1298,21 +1401,27 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
doInPromotionScope(right, () => visitExpression(right)); |
registry.registerSendStructure(node, const LogicalAndStructure()); |
+ ConstantExpression constant; |
if (leftResult.isConstant && rightResult.isConstant) { |
- ConstantExpression leftConstant = leftResult.constant; |
- ConstantExpression rightConstant = rightResult.constant; |
- if (leftConstant.getKnownType(coreTypes) == coreTypes.boolType && |
- rightConstant.getKnownType(coreTypes) == coreTypes.boolType) { |
- // TODO(johnniwinther): Handle potentially invalid constant expressions. |
- ConstantExpression constant = new BinaryConstantExpression( |
- leftConstant, |
+ bool isValidAsConstant = constantChecker.checkBinaryExpression( |
+ node, |
+ constantChecker.createInfo(leftResult), |
+ BinaryOperator.LOGICAL_AND, |
+ constantChecker.createInfo(rightResult)); |
+ if (isValidAsConstant) { |
+ constant = new BinaryConstantExpression( |
+ leftResult.constant, |
BinaryOperator.LOGICAL_AND, |
- rightConstant); |
- registry.setConstant(node, constant); |
- return new ConstantResult(node, constant); |
+ rightResult.constant); |
} |
} |
- |
+ if (constant == null && constantState.requiresConstant) { |
+ constant = new ErroneousConstantExpression(); |
+ } |
+ if (constant != null) { |
+ registry.setConstant(node, constant); |
+ return new ConstantResult(node, constant); |
+ } |
return const NoneResult(); |
} |
@@ -1324,20 +1433,27 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
ResolutionResult rightResult = visitExpression(right); |
registry.registerSendStructure(node, const LogicalOrStructure()); |
+ ConstantExpression constant; |
if (leftResult.isConstant && rightResult.isConstant) { |
- ConstantExpression leftConstant = leftResult.constant; |
- ConstantExpression rightConstant = rightResult.constant; |
- if (leftConstant.getKnownType(coreTypes) == coreTypes.boolType && |
- rightConstant.getKnownType(coreTypes) == coreTypes.boolType) { |
- // TODO(johnniwinther): Handle potentially invalid constant expressions. |
- ConstantExpression constant = new BinaryConstantExpression( |
- leftConstant, |
+ bool isValidAsConstant = constantChecker.checkBinaryExpression( |
+ node, |
+ constantChecker.createInfo(leftResult), |
+ BinaryOperator.LOGICAL_OR, |
+ constantChecker.createInfo(rightResult)); |
+ if (isValidAsConstant) { |
+ constant = new BinaryConstantExpression( |
+ leftResult.constant, |
BinaryOperator.LOGICAL_OR, |
- rightConstant); |
- registry.setConstant(node, constant); |
- return new ConstantResult(node, constant); |
+ rightResult.constant); |
} |
} |
+ if (constant == null && constantState.requiresConstant) { |
+ constant = new ErroneousConstantExpression(); |
+ } |
+ if (constant != null) { |
+ registry.setConstant(node, constant); |
+ return new ConstantResult(node, constant); |
+ } |
return const NoneResult(); |
} |
@@ -1348,7 +1464,14 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
visitExpression(left); |
visitExpression(right); |
registry.registerSendStructure(node, const IfNullStructure()); |
- return const NoneResult(); |
+ ResolutionResult result = const NoneResult(); |
+ if (constantState.requiresConstant) { |
+ ConstantExpression constant = new ErroneousConstantExpression(); |
+ reporter.reportErrorMessage(node, MessageKind.INVALID_CONSTANT_IF_NULL); |
+ registry.setConstant(node, constant); |
+ result = new ConstantResult(node, constant); |
+ } |
+ return result; |
} |
/// Handle the binary expression of an unresolved binary operator [text], like |
@@ -1404,81 +1527,28 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
registry.registerDynamicInvocation(new UniverseSelector(selector, null)); |
semantics = const DynamicAccess.expression(); |
+ ConstantExpression constant; |
if (leftResult.isConstant && rightResult.isConstant) { |
- bool isValidConstant; |
- ConstantExpression leftConstant = leftResult.constant; |
- ConstantExpression rightConstant = rightResult.constant; |
- DartType knownLeftType = leftConstant.getKnownType(coreTypes); |
- DartType knownRightType = rightConstant.getKnownType(coreTypes); |
- switch (operator.kind) { |
- case BinaryOperatorKind.EQ: |
- case BinaryOperatorKind.NOT_EQ: |
- isValidConstant = |
- (knownLeftType == coreTypes.intType || |
- knownLeftType == coreTypes.doubleType || |
- knownLeftType == coreTypes.stringType || |
- knownLeftType == coreTypes.boolType || |
- knownLeftType == coreTypes.nullType) && |
- (knownRightType == coreTypes.intType || |
- knownRightType == coreTypes.doubleType || |
- knownRightType == coreTypes.stringType || |
- knownRightType == coreTypes.boolType || |
- knownRightType == coreTypes.nullType); |
- break; |
- case BinaryOperatorKind.ADD: |
- isValidConstant = |
- (knownLeftType == coreTypes.intType || |
- knownLeftType == coreTypes.doubleType || |
- knownLeftType == coreTypes.stringType) && |
- (knownRightType == coreTypes.intType || |
- knownRightType == coreTypes.doubleType || |
- knownRightType == coreTypes.stringType); |
- break; |
- case BinaryOperatorKind.SUB: |
- case BinaryOperatorKind.MUL: |
- case BinaryOperatorKind.DIV: |
- case BinaryOperatorKind.IDIV: |
- case BinaryOperatorKind.MOD: |
- case BinaryOperatorKind.GTEQ: |
- case BinaryOperatorKind.GT: |
- case BinaryOperatorKind.LTEQ: |
- case BinaryOperatorKind.LT: |
- isValidConstant = |
- (knownLeftType == coreTypes.intType || |
- knownLeftType == coreTypes.doubleType) && |
- (knownRightType == coreTypes.intType || |
- knownRightType == coreTypes.doubleType); |
- break; |
- case BinaryOperatorKind.SHL: |
- case BinaryOperatorKind.SHR: |
- case BinaryOperatorKind.AND: |
- case BinaryOperatorKind.OR: |
- case BinaryOperatorKind.XOR: |
- isValidConstant = |
- knownLeftType == coreTypes.intType && |
- knownRightType == coreTypes.intType; |
- break; |
- case BinaryOperatorKind.INDEX: |
- isValidConstant = false; |
- break; |
- case BinaryOperatorKind.LOGICAL_AND: |
- case BinaryOperatorKind.LOGICAL_OR: |
- case BinaryOperatorKind.IF_NULL: |
- reporter.internalError( |
- node, "Unexpected binary operator '${operator}'."); |
- break; |
- } |
- if (isValidConstant) { |
- // TODO(johnniwinther): Handle potentially invalid constant |
- // expressions. |
- ConstantExpression constant = new BinaryConstantExpression( |
+ //reportHere(compiler, node, 'left=${leftResult.constant.getText()},right=${rightResult.constant.getText()}'); |
+ bool isValidAsConstant = constantChecker.checkBinaryExpression( |
+ node, |
+ constantChecker.createInfo(leftResult), |
+ operator, |
+ constantChecker.createInfo(rightResult)); |
+ if (isValidAsConstant) { |
+ constant = new BinaryConstantExpression( |
leftResult.constant, |
operator, |
rightResult.constant); |
- registry.setConstant(node, constant); |
- result = new ConstantResult(node, constant); |
} |
} |
+ if (constant == null && constantState.requiresConstant) { |
+ constant = new ErroneousConstantExpression(); |
+ } |
+ if (constant != null) { |
+ registry.setConstant(node, constant); |
+ result = new ConstantResult(node, constant); |
+ } |
} |
if (semantics != null) { |
@@ -1546,7 +1616,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
/// `this.name()`, or `name` and `name()` in instance context. |
ResolutionResult handleThisPropertyAccess(Send node, Name name) { |
AccessSemantics semantics = new DynamicAccess.thisProperty(name); |
- return handleDynamicAccessSemantics(node, name, semantics); |
+ return handleDynamicAccessSemantics( |
+ node, const NoneResult(), name, semantics); |
} |
/// Handle update of a property of [name] on `this`, like `this.name = b` and |
@@ -2059,7 +2130,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
ConstantAccess semantics) { |
// TODO(johnniwinther): Remove this when all constants are evaluated. |
- compiler.resolver.constantCompiler.evaluate(semantics.constant); |
+ compiler.resolver.constantCompiler.evaluate(node, semantics.constant); |
ErroneousElement error; |
if (node.isIfNullAssignment) { |
@@ -2131,6 +2202,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
ConstantExpression constant = |
new TypeConstantExpression(const DynamicType()); |
AccessSemantics semantics = new ConstantAccess.dynamicTypeLiteral(constant); |
+ // TODO(johnniwinther): Remove this when all constants are evaluated. |
+ compiler.resolver.constantCompiler.evaluate(node, constant); |
return handleConstantTypeLiteralUpdate( |
node, const PublicName('dynamic'), compiler.typeClass, type, semantics); |
} |
@@ -2326,7 +2399,11 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
/// Handle dynamic access of [semantics]. |
ResolutionResult handleDynamicAccessSemantics( |
- Send node, Name name, AccessSemantics semantics) { |
+ Send node, |
+ ResolutionResult receiverResult, |
+ Name name, |
+ AccessSemantics semantics) { |
+ ResolutionResult result = const NoneResult(); |
SendStructure sendStructure; |
Selector selector; |
if (node.isCall) { |
@@ -2341,13 +2418,22 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
selector = new Selector.getter(name); |
registry.registerDynamicGetter( |
new UniverseSelector(selector, null)); |
+ if (receiverResult.isConstant && name == const PublicName("length")) { |
+ if (constantChecker.isStringOrNull( |
+ receiverResult.constant.getKnownType(coreTypes))) { |
+ ConstantExpression constant = |
+ new StringLengthConstantExpression(receiverResult.constant); |
+ registry.setConstant(node, constant); |
+ result = new ConstantResult(node, constant); |
+ } |
+ } |
sendStructure = new GetStructure(semantics); |
} |
registry.registerSendStructure(node, sendStructure); |
// TODO(23998): Remove this when all information goes through |
// the [SendStructure]. |
registry.setSelector(node, selector); |
- return const NoneResult(); |
+ return result; |
} |
/// Handle dynamic update of [semantics]. |
@@ -2418,13 +2504,15 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
return handlePrefixSend(node, name, result); |
} else if (node.isConditional) { |
return handleDynamicAccessSemantics( |
- node, name, new DynamicAccess.ifNotNullProperty(name)); |
+ node, result, |
+ name, new DynamicAccess.ifNotNullProperty(name)); |
} else { |
// Handle dynamic property access, like `a.b` or `a.b()` where `a` is not |
// a prefix or class. |
// TODO(johnniwinther): Use the `element` of [result]. |
return handleDynamicAccessSemantics( |
- node, name, new DynamicAccess.dynamicProperty(name)); |
+ node, result, |
+ name, new DynamicAccess.dynamicProperty(name)); |
} |
} |
@@ -2591,7 +2679,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
break; |
case AccessKind.PARAMETER: |
case AccessKind.FINAL_PARAMETER: |
- if (constantState == ConstantState.CONSTANT_INITIALIZER) { |
+ if (constantState.parameterReferenceIsConstant) { |
ParameterElement parameter = element; |
if (parameter.isNamed) { |
result = new ConstantResult( |
@@ -2782,18 +2870,33 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
switch (semantics.kind) { |
case AccessKind.STATIC_METHOD: |
case AccessKind.TOPLEVEL_METHOD: |
+ MethodElement method = member; |
// TODO(johnniwinther): Method this should be registered as a |
// closurization. |
registry.registerStaticUse(semantics.element); |
registry.registerGetOfStaticFunction(semantics.element); |
+ result = new ConstantResult( |
+ node, |
+ new FunctionConstantExpression(method), |
+ element: method); |
break; |
case AccessKind.STATIC_FIELD: |
case AccessKind.FINAL_STATIC_FIELD: |
- case AccessKind.STATIC_GETTER: |
case AccessKind.TOPLEVEL_FIELD: |
case AccessKind.FINAL_TOPLEVEL_FIELD: |
+ FieldElement field = member; |
+ if (field.isConst) { |
+ result = new ConstantResult( |
+ node, new VariableConstantExpression(field), element: field); |
+ } else { |
+ result = new ElementResult(field); |
+ } |
+ registry.registerStaticUse(semantics.element); |
+ break; |
+ case AccessKind.STATIC_GETTER: |
case AccessKind.TOPLEVEL_GETTER: |
registry.registerStaticUse(semantics.element); |
+ result = new ElementResult(member); |
break; |
case AccessKind.STATIC_SETTER: |
case AccessKind.TOPLEVEL_SETTER: |
@@ -2802,6 +2905,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
member = reportAndCreateErroneousElement( |
node.selector, name.text, |
MessageKind.CANNOT_RESOLVE_GETTER, const {}); |
+ result = new ElementResult(member); |
break; |
default: |
reporter.internalError(node, |
@@ -2813,6 +2917,10 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
FieldElement field = member; |
result = new ConstantResult( |
node, new VariableConstantExpression(field), element: field); |
+ } else if (member.isFunction) { |
+ MethodElement function = member; |
+ result = new ConstantResult( |
+ node, new FunctionConstantExpression(function), element: function); |
} else { |
result = new ElementResult(member); |
} |
@@ -3068,16 +3176,21 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
} |
ResolutionResult visitSend(Send node) { |
+ ResolutionResult result; |
if (node.isOperator) { |
// `a && b`, `a + b`, `-a`, or `a is T`. |
- return handleOperatorSend(node); |
+ result = handleOperatorSend(node); |
} else if (node.receiver != null) { |
// `a.b`. |
- return handleQualifiedSend(node); |
+ result = handleQualifiedSend(node); |
} else { |
// `a`. |
- return handleUnqualifiedSend(node); |
+ result = handleUnqualifiedSend(node); |
} |
+ if (result.kind == ResultKind.PREFIX) { |
+ return result; |
+ } |
+ return ensureConstantResult(node, result); |
} |
/// Register read access of [target] inside a closure. |
@@ -3629,6 +3742,9 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
registry.setRedirectingTargetConstructor(node, redirectionTarget); |
if (Elements.isUnresolved(redirectionTarget)) { |
registry.registerThrowNoSuchMethod(); |
+ if (isConstConstructor) { |
+ constructor.constantConstructor = const ErroneousConstantConstructor(); |
+ } |
return const NoneResult(); |
} else { |
if (isConstConstructor && |
@@ -3709,6 +3825,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
redirectionTarget, |
callStructure, |
arguments)); |
+ } else if (isConstConstructor) { |
+ constructor.constantConstructor = const ErroneousConstantConstructor(); |
} |
return const NoneResult(); |
} |
@@ -3716,7 +3834,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
ResolutionResult visitThrow(Throw node) { |
registry.registerThrowExpression(); |
visit(node.expression); |
- return const NoneResult(); |
+ return ensureConstantResult(node, const NoneResult()); |
} |
ResolutionResult visitAwait(Await node) { |
@@ -3799,7 +3917,9 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
ResolutionResult visitNewExpression(NewExpression node) { |
bool isValidAsConstant = true; |
- ConstructorElement constructor = resolveConstructor(node).element; |
+ ConstructorResult result = resolveConstructor(node); |
+ //reportHere(reporter, node, '$result'); |
+ ConstructorElement constructor = result.element; |
final bool isSymbolConstructor = constructor == compiler.symbolConstructor; |
final bool isMirrorsUsedConstant = |
node.isConst && (constructor == compiler.mirrorsUsedConstructor); |
@@ -3811,13 +3931,26 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
} else { |
argumentsResult = resolveArguments(node.send.argumentsNode); |
} |
+ |
registry.useElement(node.send, constructor); |
if (Elements.isUnresolved(constructor)) { |
- return new ResolutionResult.forElement(constructor); |
+ if (node.isConst) { |
+ ConstantExpression constant = new ErroneousConstantExpression(); |
+ registry.setConstant(node, constant); |
+ return new ConstantResult(node, constant, element: constructor); |
+ } |
+ return ensureConstantResult(node, |
+ new ResolutionResult.forElement(constructor)); |
} |
constructor.computeType(resolution); |
if (!callSelector.applies(constructor, compiler.world)) { |
registry.registerThrowNoSuchMethod(); |
+ if (node.isConst) { |
+ // TODO(johnniwinther): Provide a better message. |
+ reporter.reportErrorMessage( |
+ node, MessageKind.NOT_A_COMPILE_TIME_CONSTANT); |
+ } |
+ isValidAsConstant = false; |
} |
// [constructor] might be the implementation element |
@@ -3877,6 +4010,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
} else if (isMirrorsUsedConstant) { |
compiler.mirrorUsageAnalyzerTask.validate(node, registry.mapping); |
} |
+ |
+ ConstantExpression constant; |
if (node.isConst) { |
analyzeConstantDeferred(node); |
@@ -3898,18 +4033,57 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
constructor.isConst && |
argumentsResult.isValidAsConstant) { |
CallStructure callStructure = argumentsResult.callStructure; |
- List<ConstantExpression> arguments = argumentsResult.constantArguments; |
- ConstructedConstantExpression constant = |
- new ConstructedConstantExpression( |
- type, |
- constructor, |
- callStructure, |
- arguments); |
- return new ConstantResult(node, constant); |
+ List<ConstantExpression> arguments = |
+ argumentsResult.constantArguments; |
+ if (constructor.isFromEnvironmentConstructor) { |
+ ConstantResult nameResult = argumentsResult.argumentResults[0]; |
+ ConstantResult defaultValueResult; |
+ ConstantExpression defaultValueConstant; |
+ if (arguments.length > 1) { |
+ defaultValueResult = argumentsResult.argumentResults[1]; |
+ defaultValueConstant = defaultValueResult.constant; |
+ } |
+ if (type == coreTypes.boolType) { |
+ isValidAsConstant = constantChecker.checkBoolFromEnvironment( |
+ constantChecker.createInfo(nameResult), |
+ constantChecker.createInfo(defaultValueResult)); |
+ if (isValidAsConstant) { |
+ constant = new BoolFromEnvironmentConstantExpression( |
+ nameResult.constant, defaultValueConstant); |
+ } |
+ } else if (type == coreTypes.intType) { |
+ isValidAsConstant = constantChecker.checkIntFromEnvironment( |
+ constantChecker.createInfo(nameResult), |
+ constantChecker.createInfo(defaultValueResult)); |
+ if (isValidAsConstant) { |
+ constant = new IntFromEnvironmentConstantExpression( |
+ nameResult.constant, defaultValueConstant); |
+ } |
+ } else { |
+ assert(type == coreTypes.stringType); |
+ isValidAsConstant = constantChecker.checkStringFromEnvironment( |
+ constantChecker.createInfo(nameResult), |
+ constantChecker.createInfo(defaultValueResult)); |
+ if (isValidAsConstant) { |
+ constant = new StringFromEnvironmentConstantExpression( |
+ nameResult.constant, defaultValueConstant); |
+ } |
+ } |
+ } else { |
+ constant = new ConstructedConstantExpression( |
+ type, constructor, callStructure, arguments); |
+ } |
+ } |
+ if (constant == null) { |
+ constant = new ErroneousConstantExpression(); |
} |
} |
- |
- return const NoneResult(); |
+ if (constant != null) { |
+ registry.setConstant(node, constant); |
+ return new ConstantResult(node, constant); |
+ } else { |
+ return ensureConstantResult(node, const NoneResult()); |
+ } |
} |
void checkConstMapKeysDontOverrideEquals(Spannable spannable, |
@@ -4054,12 +4228,14 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
}); |
analyzeConstantDeferred(node); |
sendIsMemberAccess = false; |
+ ConstantExpression constant; |
if (isValidAsConstant) { |
- ConstantExpression constant = |
- new ListConstantExpression(listType, constantExpressions); |
- registry.setConstant(node, constant); |
- return new ConstantResult(node, constant); |
+ constant = new ListConstantExpression(listType, constantExpressions); |
+ } else { |
+ constant = new ErroneousConstantExpression(); |
} |
+ registry.setConstant(node, constant); |
+ return new ConstantResult(node, constant); |
} else { |
visit(node.elements); |
sendIsMemberAccess = false; |
@@ -4071,16 +4247,33 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
ResolutionResult visitConditional(Conditional node) { |
ResolutionResult conditionResult = |
doInPromotionScope(node.condition, () => visit(node.condition)); |
+ assert(checkConstantInvariant(node.condition, conditionResult)); |
+ |
ResolutionResult thenResult = |
- doInPromotionScope(node.thenExpression, () => visit(node.thenExpression)); |
+ doInPromotionScope(node.thenExpression, |
+ () => visit(node.thenExpression)); |
+ assert(checkConstantInvariant(node.thenExpression, thenResult)); |
+ |
ResolutionResult elseResult = visit(node.elseExpression); |
+ assert(checkConstantInvariant(node.elseExpression, elseResult)); |
+ |
+ ConstantExpression constant; |
if (conditionResult.isConstant && |
thenResult.isConstant && |
elseResult.isConstant) { |
- ConstantExpression constant = new ConditionalConstantExpression( |
- conditionResult.constant, |
- thenResult.constant, |
- elseResult.constant); |
+ bool isValidAsConstant = constantChecker.checkConditional( |
+ constantChecker.createInfo(conditionResult)); |
+ if (isValidAsConstant) { |
+ constant = new ConditionalConstantExpression( |
+ conditionResult.constant, |
+ thenResult.constant, |
+ elseResult.constant); |
+ } |
+ } |
+ if (constant == null && constantState.requiresConstant) { |
+ constant = new ErroneousConstantExpression(); |
+ } |
+ if (constant != null) { |
registry.setConstant(node, constant); |
return new ConstantResult(node, constant); |
} |
@@ -4095,12 +4288,17 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
bool isValidAsConstant = true; |
List<ConstantExpression> parts = <ConstantExpression>[]; |
+ ConstantExpression constant; |
void resolvePart(Node subnode) { |
ResolutionResult result = visit(subnode); |
- if (isValidAsConstant && result.isConstant) { |
- parts.add(result.constant); |
- } else { |
- isValidAsConstant = false; |
+ if (result.isConstant) { |
+ bool isPartTypeValid = constantChecker.checkConcatenate( |
+ constantChecker.createInfo(result)); |
+ if (isPartTypeValid) { |
+ parts.add(result.constant); |
+ } else { |
+ isValidAsConstant = false; |
+ } |
} |
} |
@@ -4109,9 +4307,12 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
resolvePart(part.expression); |
resolvePart(part.string); |
} |
- |
if (isValidAsConstant) { |
- ConstantExpression constant = new ConcatenateConstantExpression(parts); |
+ constant = new ConcatenateConstantExpression(parts); |
+ } else if (constantState.requiresConstant) { |
+ constant = new ErroneousConstantExpression(); |
+ } |
+ if (constant != null) { |
registry.setConstant(node, constant); |
return new ConstantResult(node, constant); |
} |
@@ -4371,7 +4572,9 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
inConstantContext(() { |
for (LiteralMapEntry entry in node.entries) { |
ResolutionResult keyResult = visit(entry.key); |
+ assert(checkConstantInvariant(entry.key, keyResult)); |
ResolutionResult valueResult = visit(entry.value); |
+ assert(checkConstantInvariant(entry.value, valueResult)); |
if (isValidAsConstant && |
keyResult.isConstant && |
valueResult.isConstant) { |
@@ -4384,12 +4587,15 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
}); |
analyzeConstantDeferred(node); |
sendIsMemberAccess = false; |
+ ConstantExpression constant; |
if (isValidAsConstant) { |
- ConstantExpression constant = new MapConstantExpression( |
+ constant = new MapConstantExpression( |
mapType, keyExpressions, valueExpressions); |
- registry.setConstant(node, constant); |
- return new ConstantResult(node, constant); |
+ } else { |
+ constant = new ErroneousConstantExpression(); |
} |
+ registry.setConstant(node, constant); |
+ return new ConstantResult(node, constant); |
} else { |
node.visitChildren(this); |
sendIsMemberAccess = false; |
@@ -4689,3 +4895,45 @@ Element lookupInScope(DiagnosticReporter reporter, Node node, |
Scope scope, String name) { |
return Elements.unwrap(scope.lookup(name), reporter, node); |
} |
+ |
+class ConstantNodeChecker extends ConstantTypeChecker<Spannable> { |
+ final Compiler compiler; |
+ final bool constantIsRequired; |
+ |
+ ConstantNodeChecker(this.compiler, {this.constantIsRequired}); |
+ |
+ @override |
+ bool get allowUnknown => constantIsRequired; |
+ |
+ @override |
+ CoreTypes get coreTypes => compiler.coreTypes; |
+ |
+ @override |
+ bool get isRequired => constantIsRequired; |
+ |
+ @override |
+ void reportError(Spannable position, MessageKind messageKind, Map arguments) { |
+ if (constantIsRequired) { |
+ compiler.reporter.reportErrorMessage(position, messageKind, arguments); |
+ } |
+ } |
+ |
+ ConstantTypeInfo createInfo(ResolutionResult result) { |
+ if (result == null) return null; |
+ return new NodeConstantTypeInfo( |
+ result.node, result.constant, result.constant.getKnownType(coreTypes)); |
+ } |
+} |
+ |
+class NodeConstantTypeInfo implements ConstantTypeInfo<Spannable> { |
+ final Spannable position; |
+ final ConstantExpression constant; |
+ final DartType type; |
+ |
+ NodeConstantTypeInfo(this.position, this.constant, this.type); |
+ |
+ String toString() { |
+ return 'NodeConstantTypeInfo[' |
+ 'position=$position,constant=${constant.getText()},type=$type]'; |
+ } |
+} |