| 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]';
|
| + }
|
| +}
|
|
|