anchor.diff:    }
   
     visitStringConcatenation(StringConcatenation node) {
  -    final String value = node.expressions.map((Expression node) {
  -      final Constant constant = _evaluateSubexpression(node);
  -
  -      if (constant is NullConstant) {
  -        return 'null';
  -      } else if (constant is BoolConstant) {
  -        return constant.value ? 'true' : 'false';
  -      } else if (constant is IntConstant) {
  -        return constant.value.toString();
  -      } else if (constant is DoubleConstant) {
  -        return constant.value.toString();
  -      } else if (constant is StringConstant) {
  -        return constant.value;
  +    final List<Object> concatenated = <Object>[new StringBuffer()];
  +    for (int i = 0; i < node.expressions.length; i++) {
  +      Constant constant = _evaluateSubexpression(node.expressions[i]);
  +      if (constant is PrimitiveConstant) {
  +        String value = constant.value.toString();
  +        Object last = concatenated.last;
  +        if (last is StringBuffer) {
  +          last.write(value);
  +        } else {
  +          concatenated.add(new StringBuffer(value));
  +        }
  +      } else if (constant is UnevaluatedConstant) {
  +        concatenated.add(constant);
         } else {
           throw new _AbortCurrentEvaluation(errorReporter
               .invalidStringInterpolationOperand(contextChain, node, constant));
         }
  -    }).join('');
  -    return canonicalize(new StringConstant(value));
  +    }
  +    if (concatenated.length > 1) {
  +      final expressions = new List<Expression>(concatenated.length);
  +      for (int i = 0; i < concatenated.length; i++) {
  +        Object value = concatenated[i];
  +        if (value is UnevaluatedConstant) {
  +          expressions[i] = unique(value.expression);
  +        } else {
  +          expressions[i] = new ConstantExpression(
  +              canonicalize(new StringConstant(value.toString())));
  +        }
  +      }
  +      return unevaluated(node, new StringConcatenation(expressions));
  +    }
  +    return canonicalize(new StringConstant(concatenated.single.toString()));
     }
   
     visitStaticInvocation(StaticInvocation node) {
  
positive.code:     final positional = new List<Expression>(positionalArgs.length);
      final named = new List<NamedExpression>(namedArgs.length);
      for (int i = 0; i < positionalArgs.length; ++i) {
        positional[i] = positionalArgs[i].asExpression();
      }
      int i = 0;
      namedArgs.forEach((String name, Constant value) {
        named[i++] = new NamedExpression(name, value.asExpression());
      });
      return new Arguments(positional, named: named, types: types);
    }
  
negative.code:           'The number of type arguments supplied in the partial instantiation '
            'does not match the number of type arguments of the $constant.');
      }
      if (constant is UnevaluatedConstant) {
        return unevaluated(node,
            new Instantiation(unique(constant.expression), node.typeArguments));
      }
      throw new Exception(
          'Only tear-off constants can be partially instantiated.');
    }
  