| Index: pkg/compiler/lib/src/constants/evaluation.dart
 | 
| diff --git a/pkg/compiler/lib/src/constants/evaluation.dart b/pkg/compiler/lib/src/constants/evaluation.dart
 | 
| index 176d736ed2599de93cbac9028f9bb9b7b7d25a56..1636d61d663500d057dfd9568c3eda10639fb309 100644
 | 
| --- a/pkg/compiler/lib/src/constants/evaluation.dart
 | 
| +++ b/pkg/compiler/lib/src/constants/evaluation.dart
 | 
| @@ -6,17 +6,45 @@ library dart2js.constants.evaluation;
 | 
|  
 | 
|  import '../compiler.dart' show
 | 
|      Compiler;
 | 
| +import '../core_types.dart';
 | 
| +import '../dart_types.dart';
 | 
| +import '../diagnostics/messages.dart' show
 | 
| +    MessageKind;
 | 
| +import '../elements/elements.dart' show
 | 
| +    ConstructorElement,
 | 
| +    VariableElement;
 | 
| +import '../resolution/operators.dart';
 | 
|  import '../universe/call_structure.dart' show
 | 
|      CallStructure;
 | 
|  import 'expressions.dart';
 | 
| +import 'values.dart';
 | 
|  
 | 
|  /// Environment used for evaluating constant expressions.
 | 
|  abstract class Environment {
 | 
|    // TODO(johnniwinther): Replace this with [CoreTypes] and maybe [Backend].
 | 
|    Compiler get compiler;
 | 
|  
 | 
| +  /// The core types in the evaluation environment.
 | 
| +  CoreTypes get coreTypes;
 | 
| +
 | 
|    /// Read environments string passed in using the '-Dname=value' option.
 | 
|    String readFromEnvironment(String name);
 | 
| +
 | 
| +  void reportWarning(ConstantExpression expression,
 | 
| +                     MessageKind kind,
 | 
| +                     Map arguments);
 | 
| +
 | 
| +  void reportError(ConstantExpression expression,
 | 
| +                   MessageKind kind,
 | 
| +                   Map arguments);
 | 
| +
 | 
| +  ConstantValue evaluateConstructor(
 | 
| +      ConstructorElement constructor,
 | 
| +      ConstantValue evaluate());
 | 
| +
 | 
| +  ConstantValue evaluateVariable(
 | 
| +      VariableElement variable,
 | 
| +      ConstantValue evaluate());
 | 
|  }
 | 
|  
 | 
|  /// The normalized arguments passed to a const constructor computed from the
 | 
| @@ -47,3 +75,496 @@ class NormalizedArguments {
 | 
|      return arguments[index];
 | 
|    }
 | 
|  }
 | 
| +
 | 
| +abstract class ConstantTypeChecker<T> {
 | 
| +  CoreTypes get coreTypes;
 | 
| +  bool get isRequired;
 | 
| +  bool get allowUnknown;
 | 
| +
 | 
| +  void reportError(T position, MessageKind messageKind, Map arguments);
 | 
| +
 | 
| +  bool isBool(DartType type, {bool allowUnknown}) {
 | 
| +    if (allowUnknown == null) allowUnknown = this.allowUnknown;
 | 
| +    return
 | 
| +        type == coreTypes.boolType ||
 | 
| +        (type == null && allowUnknown);
 | 
| +  }
 | 
| +
 | 
| +  bool isBoolOrNull(DartType type, {bool allowUnknown}) {
 | 
| +    if (allowUnknown == null) allowUnknown = this.allowUnknown;
 | 
| +    return
 | 
| +        type == coreTypes.boolType ||
 | 
| +        type == coreTypes.nullType ||
 | 
| +        (type == null && allowUnknown);
 | 
| +  }
 | 
| +
 | 
| +  bool isInt(DartType type, {bool allowUnknown}) {
 | 
| +    if (allowUnknown == null) allowUnknown = this.allowUnknown;
 | 
| +    return
 | 
| +        type == coreTypes.intType ||
 | 
| +        (type == null && allowUnknown);
 | 
| +  }
 | 
| +
 | 
| +  bool isIntOrNull(DartType type, {bool allowUnknown}) {
 | 
| +    if (allowUnknown == null) allowUnknown = this.allowUnknown;
 | 
| +    return
 | 
| +        type == coreTypes.intType ||
 | 
| +        type == coreTypes.nullType ||
 | 
| +        (type == null && allowUnknown);
 | 
| +  }
 | 
| +
 | 
| +  bool isNum(DartType type, {bool allowUnknown}) {
 | 
| +    if (allowUnknown == null) allowUnknown = this.allowUnknown;
 | 
| +    return
 | 
| +        type == coreTypes.intType ||
 | 
| +        type == coreTypes.doubleType ||
 | 
| +        (type == null && allowUnknown);
 | 
| +  }
 | 
| +
 | 
| +  bool isString(DartType type, {bool allowUnknown}) {
 | 
| +    if (allowUnknown == null) allowUnknown = this.allowUnknown;
 | 
| +    return
 | 
| +        type == coreTypes.stringType ||
 | 
| +        (type == null && allowUnknown);
 | 
| +  }
 | 
| +
 | 
| +  bool isStringOrNull(DartType type, {bool allowUnknown}) {
 | 
| +    if (allowUnknown == null) allowUnknown = this.allowUnknown;
 | 
| +    return
 | 
| +        type == coreTypes.stringType ||
 | 
| +        type == coreTypes.nullType ||
 | 
| +        (type == null && allowUnknown);
 | 
| +  }
 | 
| +
 | 
| +  bool isPrimitive(DartType type, {bool allowUnknown}) {
 | 
| +    if (allowUnknown == null) allowUnknown = this.allowUnknown;
 | 
| +    return
 | 
| +        type == coreTypes.intType ||
 | 
| +        type == coreTypes.doubleType ||
 | 
| +        type == coreTypes.stringType ||
 | 
| +        type == coreTypes.boolType ||
 | 
| +        type == coreTypes.nullType ||
 | 
| +        (type == null && allowUnknown);
 | 
| +  }
 | 
| +
 | 
| +  bool isNonNullPrimitive(DartType type, {bool allowUnknown}) {
 | 
| +    if (allowUnknown == null) allowUnknown = this.allowUnknown;
 | 
| +    return
 | 
| +        type == coreTypes.boolType ||
 | 
| +        type == coreTypes.intType ||
 | 
| +        type == coreTypes.doubleType ||
 | 
| +        type == coreTypes.stringType ||
 | 
| +        (type == null && allowUnknown);
 | 
| +  }
 | 
| +
 | 
| +  bool checkBinaryExpression(
 | 
| +      T position,
 | 
| +      ConstantTypeInfo<T> left,
 | 
| +      BinaryOperator operator,
 | 
| +      ConstantTypeInfo<T> right) {
 | 
| +    bool isValid = true;
 | 
| +    switch (operator.kind) {
 | 
| +      case BinaryOperatorKind.EQ:
 | 
| +      case BinaryOperatorKind.NOT_EQ:
 | 
| +        if (!isPrimitive(left.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                left.position,
 | 
| +                MessageKind.INVALID_CONSTANT_BINARY_PRIMITIVE_TYPE,
 | 
| +                {'constant': left.constant,
 | 
| +                 'type': left.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        if (!isPrimitive(right.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                right.position,
 | 
| +                MessageKind.INVALID_CONSTANT_BINARY_PRIMITIVE_TYPE,
 | 
| +                {'constant': right.constant,
 | 
| +                 'type': right.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        break;
 | 
| +      case BinaryOperatorKind.ADD:
 | 
| +        if (isString(left.type, allowUnknown: false)) {
 | 
| +          if (!isString(right.type)) {
 | 
| +            if (isRequired) {
 | 
| +              reportError(
 | 
| +                  right.position,
 | 
| +                  MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE,
 | 
| +                  {'constant': right.constant,
 | 
| +                   'type': right.type});
 | 
| +            }
 | 
| +            isValid = false;
 | 
| +          }
 | 
| +        } else if (isNum(left.type, allowUnknown: false)) {
 | 
| +          if (!isNum(right.type)) {
 | 
| +            if (isRequired) {
 | 
| +              reportError(
 | 
| +                  right.position,
 | 
| +                  MessageKind.INVALID_CONSTANT_NUM_ADD_TYPE,
 | 
| +                  {'constant': right.constant,
 | 
| +                   'type': right.type});
 | 
| +            }
 | 
| +            isValid = false;
 | 
| +          }
 | 
| +        } else if (isString(right.type, allowUnknown: false)) {
 | 
| +          if (!isString(left.type)) {
 | 
| +            if (isRequired) {
 | 
| +              reportError(
 | 
| +                  left.position,
 | 
| +                  MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE,
 | 
| +                  {'constant': left.constant,
 | 
| +                   'type': left.type});
 | 
| +            }
 | 
| +            isValid = false;
 | 
| +          }
 | 
| +        } else if (isNum(right.type, allowUnknown: false)) {
 | 
| +          if (!isNum(left.type)) {
 | 
| +            if (isRequired) {
 | 
| +              reportError(
 | 
| +                  left.position,
 | 
| +                  MessageKind.INVALID_CONSTANT_NUM_ADD_TYPE,
 | 
| +                  {'constant': left.constant,
 | 
| +                   'type': left.type});
 | 
| +            }
 | 
| +            isValid = false;
 | 
| +          }
 | 
| +        } else if (allowUnknown && (left.type == null || right.type == null)) {
 | 
| +          // Assume valid on unknown types.
 | 
| +        } else {
 | 
| +          if (isRequired) {
 | 
| +            assert(left.type != null);
 | 
| +            assert(right.type != null);
 | 
| +            reportError(
 | 
| +                position,
 | 
| +                MessageKind.INVALID_CONSTANT_ADD_TYPES,
 | 
| +                {'leftConstant': left.constant,
 | 
| +                 'leftType': left.type,
 | 
| +                 'rightConstant': right.constant,
 | 
| +                 'rightType': right.type});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        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:
 | 
| +        if (!isNum(left.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                left.position,
 | 
| +                MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE,
 | 
| +                {'constant': left.constant,
 | 
| +                 'type': left.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        if (!isNum(right.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                right.position,
 | 
| +                MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE,
 | 
| +                {'constant': right.constant,
 | 
| +                 'type': right.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        break;
 | 
| +      case BinaryOperatorKind.SHL:
 | 
| +      case BinaryOperatorKind.SHR:
 | 
| +      case BinaryOperatorKind.AND:
 | 
| +      case BinaryOperatorKind.OR:
 | 
| +      case BinaryOperatorKind.XOR:
 | 
| +        if (!isInt(left.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                left.position,
 | 
| +                MessageKind.INVALID_CONSTANT_BINARY_INT_TYPE,
 | 
| +                {'constant': left.constant,
 | 
| +                 'type': left.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        if (!isInt(right.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                right.position,
 | 
| +                MessageKind.INVALID_CONSTANT_BINARY_INT_TYPE,
 | 
| +                {'constant': right.constant,
 | 
| +                 'type': right.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        break;
 | 
| +      case BinaryOperatorKind.LOGICAL_AND:
 | 
| +        if (!isBool(left.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                left.position,
 | 
| +                MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE,
 | 
| +                {'constant': left.constant,
 | 
| +                 'type': left.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        if (!isBool(right.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                right.position,
 | 
| +                MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE,
 | 
| +                {'constant': right.constant,
 | 
| +                 'type': right.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        break;
 | 
| +     case BinaryOperatorKind.LOGICAL_OR:
 | 
| +        if (!isBool(left.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                left.position,
 | 
| +                MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE,
 | 
| +                {'constant': left.constant,
 | 
| +                 'type': left.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        if (!isBool(right.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                right.position,
 | 
| +                MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE,
 | 
| +                {'constant': right.constant,
 | 
| +                 'type': right.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        break;
 | 
| +      case BinaryOperatorKind.INDEX:
 | 
| +        if (isRequired) {
 | 
| +          reportError(position, MessageKind.INVALID_CONSTANT_INDEX, {});
 | 
| +        }
 | 
| +        isValid = false;
 | 
| +        break;
 | 
| +      case BinaryOperatorKind.IF_NULL:
 | 
| +        if (isRequired) {
 | 
| +          reportError(position, MessageKind.INVALID_CONSTANT_IF_NULL, {});
 | 
| +        }
 | 
| +        isValid = false;
 | 
| +        break;
 | 
| +    }
 | 
| +    return isValid;
 | 
| +  }
 | 
| +
 | 
| +  bool checkFromEnvironment(ConstantTypeInfo<T> name) {
 | 
| +    bool isValidAsConstant = true;
 | 
| +    if (!isString(name.type)) {
 | 
| +      if (isRequired) {
 | 
| +        reportError(
 | 
| +            name.position,
 | 
| +            MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE,
 | 
| +            {'constant': name.constant,
 | 
| +             'type': name.type});
 | 
| +      }
 | 
| +      isValidAsConstant = false;
 | 
| +    }
 | 
| +    return isValidAsConstant;
 | 
| +  }
 | 
| +
 | 
| +  bool checkBoolFromEnvironment(
 | 
| +      ConstantTypeInfo<T> name,
 | 
| +      ConstantTypeInfo<T> defaultValue) {
 | 
| +    bool isValidAsConstant = checkFromEnvironment(name);
 | 
| +    if (defaultValue != null) {
 | 
| +      if (!isBoolOrNull(defaultValue.type)) {
 | 
| +        if (isRequired) {
 | 
| +          reportError(
 | 
| +              defaultValue.position,
 | 
| +              MessageKind.INVALID_BOOL_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE,
 | 
| +              {'constant': defaultValue.constant,
 | 
| +               'type': defaultValue.type});
 | 
| +        }
 | 
| +        isValidAsConstant = false;
 | 
| +      }
 | 
| +    }
 | 
| +    return isValidAsConstant;
 | 
| +  }
 | 
| +
 | 
| +  bool checkIntFromEnvironment(
 | 
| +      ConstantTypeInfo<T> name,
 | 
| +      ConstantTypeInfo<T> defaultValue) {
 | 
| +    bool isValidAsConstant = checkFromEnvironment(name);
 | 
| +    if (defaultValue != null) {
 | 
| +      if (!isIntOrNull(defaultValue.type)) {
 | 
| +        if (isRequired) {
 | 
| +          reportError(
 | 
| +              defaultValue.position,
 | 
| +              MessageKind.INVALID_INT_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE,
 | 
| +              {'constant': defaultValue.constant,
 | 
| +               'type': defaultValue.type});
 | 
| +        }
 | 
| +        isValidAsConstant = false;
 | 
| +      }
 | 
| +    }
 | 
| +    return isValidAsConstant;
 | 
| +  }
 | 
| +
 | 
| +  bool checkStringFromEnvironment(
 | 
| +      ConstantTypeInfo<T> name,
 | 
| +      ConstantTypeInfo<T> defaultValue) {
 | 
| +    bool isValidAsConstant = checkFromEnvironment(name);
 | 
| +    if (defaultValue != null) {
 | 
| +      if (!isStringOrNull(defaultValue.type)) {
 | 
| +        if (isRequired) {
 | 
| +          reportError(
 | 
| +              defaultValue.position,
 | 
| +              MessageKind.INVALID_STRING_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE,
 | 
| +              {'constant': defaultValue.constant,
 | 
| +               'type': defaultValue.type});
 | 
| +        }
 | 
| +        isValidAsConstant = false;
 | 
| +      }
 | 
| +    }
 | 
| +    return isValidAsConstant;
 | 
| +  }
 | 
| +
 | 
| +  bool checkConcatenate(ConstantTypeInfo<T> part) {
 | 
| +    bool isValidAsConstant = true;
 | 
| +    if (!isNonNullPrimitive(part.type)) {
 | 
| +      if (isRequired) {
 | 
| +        reportError(
 | 
| +            part.position,
 | 
| +            MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE,
 | 
| +            {'constant': part.constant,
 | 
| +             'type': part.type});
 | 
| +      }
 | 
| +      isValidAsConstant = false;
 | 
| +    }
 | 
| +    return isValidAsConstant;
 | 
| +  }
 | 
| +
 | 
| +  bool checkConditional(ConstantTypeInfo<T> condition) {
 | 
| +    bool isValidAsConstant = true;
 | 
| +    if (!isBool(condition.type)) {
 | 
| +      reportError(
 | 
| +          condition.position,
 | 
| +          MessageKind.INVALID_CONSTANT_CONDITIONAL_TYPE,
 | 
| +          {'constant': condition.constant,
 | 
| +           'type': condition.type});
 | 
| +      isValidAsConstant = false;
 | 
| +    }
 | 
| +    return isValidAsConstant;
 | 
| +  }
 | 
| +
 | 
| +
 | 
| +  bool checkUnary(
 | 
| +      UnaryOperator operator,
 | 
| +      ConstantTypeInfo<T> expression) {
 | 
| +    bool isValid = true;
 | 
| +    switch (operator.kind) {
 | 
| +      case UnaryOperatorKind.NOT:
 | 
| +        if (!isBool(expression.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                expression.position,
 | 
| +                MessageKind.INVALID_CONSTANT_NOT_TYPE,
 | 
| +                {'constant': expression.constant,
 | 
| +                 'type': expression.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        break;
 | 
| +      case UnaryOperatorKind.NEGATE:
 | 
| +        if (!isNum(expression.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                expression.position,
 | 
| +                MessageKind.INVALID_CONSTANT_NEGATE_TYPE,
 | 
| +                {'constant': expression.constant,
 | 
| +                 'type': expression.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        break;
 | 
| +      case UnaryOperatorKind.COMPLEMENT:
 | 
| +        if (!isInt(expression.type)) {
 | 
| +          if (isRequired) {
 | 
| +            reportError(
 | 
| +                expression.position,
 | 
| +                MessageKind.INVALID_CONSTANT_COMPLEMENT_TYPE,
 | 
| +                {'constant': expression.constant,
 | 
| +                 'type': expression.type,
 | 
| +                 'operator': operator});
 | 
| +          }
 | 
| +          isValid = false;
 | 
| +        }
 | 
| +        break;
 | 
| +    }
 | 
| +    return isValid;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +abstract class ConstantTypeInfo<T> {
 | 
| +  T get position;
 | 
| +  ConstantExpression get constant;
 | 
| +  DartType get type;
 | 
| +}
 | 
| +
 | 
| +class ConstantExpressionChecker
 | 
| +    extends ConstantTypeChecker<ConstantExpression> {
 | 
| +  final Environment environment;
 | 
| +
 | 
| +  ConstantExpressionChecker(this.environment);
 | 
| +
 | 
| +  @override
 | 
| +  bool get allowUnknown => false;
 | 
| +
 | 
| +  @override
 | 
| +  CoreTypes get coreTypes => environment.coreTypes;
 | 
| +
 | 
| +  @override
 | 
| +  bool get isRequired => true;
 | 
| +
 | 
| +  @override
 | 
| +  void reportError(ConstantExpression position,
 | 
| +                   MessageKind messageKind,
 | 
| +                   Map arguments) {
 | 
| +    environment.reportError(position, messageKind, arguments);
 | 
| +  }
 | 
| +
 | 
| +  ConstantValueTypeInfo createInfo(ConstantExpression expression,
 | 
| +                                   ConstantValue value) {
 | 
| +    if (expression == null) return null;
 | 
| +    return new ConstantValueTypeInfo(expression, value.getType(coreTypes));
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +class ConstantValueTypeInfo implements ConstantTypeInfo<ConstantExpression> {
 | 
| +  final ConstantExpression constant;
 | 
| +  final DartType type;
 | 
| +
 | 
| +  ConstantValueTypeInfo(this.constant, this.type);
 | 
| +
 | 
| +  ConstantExpression get position => constant;
 | 
| +}
 | 
| 
 |