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