[CFE] Refactor const collection construction into builders.

This refactoring does not change the behavior of the constant
evaluator, but is paves the way for the fix of
https://github.com/dart-lang/sdk/issues/36812

Change-Id: Ie621a352ce6b69ce7e9bd97ffdc688b327e3a7dd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101260
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_collection_builders.dart b/pkg/front_end/lib/src/fasta/kernel/constant_collection_builders.dart
new file mode 100644
index 0000000..7d83bc2
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_collection_builders.dart
@@ -0,0 +1,427 @@
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of 'constant_evaluator.dart';
+
+abstract class _ListOrSetConstantBuilder<L extends Expression,
+    C extends Constant> {
+  final ConstantEvaluator evaluator;
+  final Expression original;
+  final DartType elementType;
+
+  // Each element of [parts] is either a `List<Constant>` (containing fully
+  // evaluated constants) or a `Constant` (potentially unevaluated).
+  List<Object> parts = <Object>[<Constant>[]];
+
+  _ListOrSetConstantBuilder(this.original, this.elementType, this.evaluator);
+
+  L makeLiteral(List<Expression> elements);
+
+  C makeConstant(List<Constant> elements);
+
+  Message get messageForIteration;
+
+  /// Add an element (which is possibly a spread or an if element) to the
+  /// constant list being built by this builder.
+  void add(Expression element) {
+    if (element is SpreadElement) {
+      addSpread(element.expression, isNullAware: element.isNullAware);
+    } else if (element is IfElement) {
+      Constant condition = evaluator._evaluateSubexpression(element.condition);
+      if (evaluator.shouldBeUnevaluated) {
+        // Unevaluated if
+        evaluator.enterLazy();
+        Constant then = evaluator._evaluateSubexpression(
+            makeLiteral([evaluator.cloner.clone(element.then)]));
+        Constant otherwise;
+        if (element.otherwise != null) {
+          otherwise = evaluator._evaluateSubexpression(
+              makeLiteral([evaluator.cloner.clone(element.otherwise)]));
+        } else {
+          otherwise = makeConstant([]);
+        }
+        evaluator.leaveLazy();
+        parts.add(evaluator.unevaluated(
+            element.condition,
+            new ConditionalExpression(
+                evaluator.extract(condition),
+                evaluator.extract(then),
+                evaluator.extract(otherwise),
+                const DynamicType())));
+      } else {
+        // Fully evaluated if
+        if (condition == evaluator.trueConstant) {
+          add(element.then);
+        } else if (condition == evaluator.falseConstant) {
+          if (element.otherwise != null) {
+            add(element.otherwise);
+          }
+        } else if (condition == evaluator.nullConstant) {
+          evaluator.report(element.condition, messageConstEvalNullValue);
+        } else {
+          evaluator.report(
+              element.condition,
+              templateConstEvalInvalidType.withArguments(
+                  condition,
+                  evaluator.typeEnvironment.boolType,
+                  condition.getType(evaluator.typeEnvironment)));
+        }
+      }
+    } else if (element is ForElement || element is ForInElement) {
+      // For or for-in
+      evaluator.report(element, messageForIteration);
+    } else {
+      // Ordinary expression element
+      Constant constant = evaluator._evaluateSubexpression(element);
+      if (evaluator.shouldBeUnevaluated) {
+        parts.add(evaluator.unevaluated(
+            element, makeLiteral([evaluator.extract(constant)])));
+      } else {
+        addConstant(constant, element);
+      }
+    }
+  }
+
+  void addSpread(Expression spreadExpression, {bool isNullAware}) {
+    Constant spread =
+        evaluator.unlower(evaluator._evaluateSubexpression(spreadExpression));
+    if (evaluator.shouldBeUnevaluated) {
+      // Unevaluated spread
+      if (isNullAware) {
+        VariableDeclaration temp = new VariableDeclaration(null,
+            initializer: evaluator.extract(spread));
+        parts.add(evaluator.unevaluated(
+            spreadExpression,
+            new Let(
+                temp,
+                new ConditionalExpression(
+                    new MethodInvocation(new VariableGet(temp), new Name('=='),
+                        new Arguments([new NullLiteral()])),
+                    new ListLiteral([], isConst: true),
+                    new VariableGet(temp),
+                    const DynamicType()))));
+      } else {
+        parts.add(spread);
+      }
+    } else if (spread == evaluator.nullConstant) {
+      // Null spread
+      if (!isNullAware) {
+        evaluator.report(spreadExpression, messageConstEvalNullValue);
+      }
+    } else {
+      // Fully evaluated spread
+      List<Constant> entries;
+      if (spread is ListConstant) {
+        entries = spread.entries;
+      } else if (spread is SetConstant) {
+        entries = spread.entries;
+      } else {
+        // Not list or set in spread
+        return evaluator.report(
+            spreadExpression, messageConstEvalNotListOrSetInSpread);
+      }
+      for (Constant entry in entries) {
+        addConstant(entry, spreadExpression);
+      }
+    }
+  }
+
+  void addConstant(Constant constant, TreeNode context);
+}
+
+class ListConstantBuilder
+    extends _ListOrSetConstantBuilder<ListLiteral, ListConstant> {
+  ListConstantBuilder(
+      Expression original, DartType elementType, ConstantEvaluator evaluator)
+      : super(original, elementType, evaluator);
+
+  @override
+  ListLiteral makeLiteral(List<Expression> elements) =>
+      new ListLiteral(elements, isConst: true);
+
+  @override
+  ListConstant makeConstant(List<Constant> elements) =>
+      new ListConstant(const DynamicType(), elements);
+
+  @override
+  Message get messageForIteration => messageConstEvalIterationInConstList;
+
+  @override
+  void addConstant(Constant constant, TreeNode context) {
+    List<Constant> lastPart;
+    if (parts.last is List<Constant>) {
+      lastPart = parts.last;
+    } else {
+      parts.add(lastPart = <Constant>[]);
+    }
+    lastPart.add(evaluator.ensureIsSubtype(constant, elementType, context));
+  }
+
+  Constant build() {
+    if (parts.length == 1) {
+      // Fully evaluated
+      return evaluator
+          .lowerListConstant(new ListConstant(elementType, parts.single));
+    }
+    List<Expression> lists = <Expression>[];
+    for (Object part in parts) {
+      if (part is List<Constant>) {
+        lists.add(new ConstantExpression(new ListConstant(elementType, part)));
+      } else if (part is Constant) {
+        lists.add(evaluator.extract(part));
+      } else {
+        throw 'Non-constant in constant list';
+      }
+    }
+    return evaluator.unevaluated(
+        original, new ListConcatenation(lists, typeArgument: elementType));
+  }
+}
+
+class SetConstantBuilder
+    extends _ListOrSetConstantBuilder<SetLiteral, SetConstant> {
+  final Set<Constant> seen = new Set<Constant>.identity();
+
+  SetConstantBuilder(
+      Expression original, DartType elementType, ConstantEvaluator evaluator)
+      : super(original, elementType, evaluator);
+
+  @override
+  SetLiteral makeLiteral(List<Expression> elements) =>
+      new SetLiteral(elements, isConst: true);
+
+  @override
+  SetConstant makeConstant(List<Constant> elements) =>
+      new SetConstant(const DynamicType(), elements);
+
+  @override
+  Message get messageForIteration => messageConstEvalIterationInConstSet;
+
+  @override
+  void addConstant(Constant constant, TreeNode context) {
+    if (!evaluator.hasPrimitiveEqual(constant)) {
+      evaluator.report(context,
+          templateConstEvalElementImplementsEqual.withArguments(constant));
+    }
+    if (!seen.add(constant)) {
+      evaluator.report(
+          context, templateConstEvalDuplicateElement.withArguments(constant));
+    }
+
+    List<Constant> lastPart;
+    if (parts.last is List<Constant>) {
+      lastPart = parts.last;
+    } else {
+      parts.add(lastPart = <Constant>[]);
+    }
+    lastPart.add(evaluator.ensureIsSubtype(constant, elementType, context));
+  }
+
+  Constant build() {
+    if (parts.length == 1) {
+      // Fully evaluated
+      List<Constant> entries = parts.single;
+      SetConstant result = new SetConstant(elementType, entries);
+      if (evaluator.desugarSets) {
+        final List<ConstantMapEntry> mapEntries =
+            new List<ConstantMapEntry>(entries.length);
+        for (int i = 0; i < entries.length; ++i) {
+          mapEntries[i] =
+              new ConstantMapEntry(entries[i], evaluator.nullConstant);
+        }
+        Constant map = evaluator.lowerMapConstant(new MapConstant(
+            elementType, evaluator.typeEnvironment.nullType, mapEntries));
+        return evaluator.lower(
+            result,
+            new InstanceConstant(
+                evaluator.unmodifiableSetMap.enclosingClass.reference, [
+              elementType
+            ], <Reference, Constant>{
+              evaluator.unmodifiableSetMap.reference: map
+            }));
+      } else {
+        return evaluator.lowerSetConstant(result);
+      }
+    }
+    List<Expression> sets = <Expression>[];
+    for (Object part in parts) {
+      if (part is List<Constant>) {
+        sets.add(new ConstantExpression(new SetConstant(elementType, part)));
+      } else if (part is Constant) {
+        sets.add(evaluator.extract(part));
+      } else {
+        throw 'Non-constant in constant set';
+      }
+    }
+    return evaluator.unevaluated(
+        original, new SetConcatenation(sets, typeArgument: elementType));
+  }
+}
+
+class MapConstantBuilder {
+  final ConstantEvaluator evaluator;
+  final Expression original;
+  final DartType keyType;
+  final DartType valueType;
+
+  /// Each element of [parts] is either a `List<ConstantMapEntry>` (containing
+  /// fully evaluated map entries) or a `Constant` (potentially unevaluated).
+  List<Object> parts = <Object>[<ConstantMapEntry>[]];
+
+  final Set<Constant> seenKeys = new Set<Constant>.identity();
+
+  MapConstantBuilder(
+      this.original, this.keyType, this.valueType, this.evaluator);
+
+  /// Add a map entry (which is possibly a spread or an if map entry) to the
+  /// constant map being built by this builder
+  void add(MapEntry element) {
+    if (element is SpreadMapEntry) {
+      addSpread(element.expression, isNullAware: element.isNullAware);
+    } else if (element is IfMapEntry) {
+      Constant condition = evaluator._evaluateSubexpression(element.condition);
+      if (evaluator.shouldBeUnevaluated) {
+        // Unevaluated if
+        evaluator.enterLazy();
+        Constant then = evaluator._evaluateSubexpression(new MapLiteral(
+            [evaluator.cloner.clone(element.then)],
+            isConst: true));
+        Constant otherwise;
+        if (element.otherwise != null) {
+          otherwise = evaluator._evaluateSubexpression(new MapLiteral(
+              [evaluator.cloner.clone(element.otherwise)],
+              isConst: true));
+        } else {
+          otherwise =
+              new MapConstant(const DynamicType(), const DynamicType(), []);
+        }
+        evaluator.leaveLazy();
+        parts.add(evaluator.unevaluated(
+            element.condition,
+            new ConditionalExpression(
+                evaluator.extract(condition),
+                evaluator.extract(then),
+                evaluator.extract(otherwise),
+                const DynamicType())));
+      } else {
+        // Fully evaluated if
+        if (condition == evaluator.trueConstant) {
+          add(element.then);
+        } else if (condition == evaluator.falseConstant) {
+          if (element.otherwise != null) {
+            add(element.otherwise);
+          }
+        } else if (condition == evaluator.nullConstant) {
+          evaluator.report(element.condition, messageConstEvalNullValue);
+        } else {
+          evaluator.report(
+              element.condition,
+              templateConstEvalInvalidType.withArguments(
+                  condition,
+                  evaluator.typeEnvironment.boolType,
+                  condition.getType(evaluator.typeEnvironment)));
+        }
+      }
+    } else if (element is ForMapEntry || element is ForInMapEntry) {
+      // For or for-in
+      evaluator.report(element, messageConstEvalIterationInConstMap);
+    } else {
+      // Ordinary map entry
+      Constant key = evaluator._evaluateSubexpression(element.key);
+      Constant value = evaluator._evaluateSubexpression(element.value);
+      if (evaluator.shouldBeUnevaluated) {
+        parts.add(evaluator.unevaluated(
+            element.key,
+            new MapLiteral([
+              new MapEntry(evaluator.extract(key), evaluator.extract(value))
+            ], isConst: true)));
+      } else {
+        addConstant(key, value, element.key, element.value);
+      }
+    }
+  }
+
+  void addSpread(Expression spreadExpression, {bool isNullAware}) {
+    Constant spread =
+        evaluator.unlower(evaluator._evaluateSubexpression(spreadExpression));
+    if (evaluator.shouldBeUnevaluated) {
+      // Unevaluated spread
+      if (isNullAware) {
+        VariableDeclaration temp = new VariableDeclaration(null,
+            initializer: evaluator.extract(spread));
+        parts.add(evaluator.unevaluated(
+            spreadExpression,
+            new Let(
+                temp,
+                new ConditionalExpression(
+                    new MethodInvocation(new VariableGet(temp), new Name('=='),
+                        new Arguments([new NullLiteral()])),
+                    new MapLiteral([], isConst: true),
+                    new VariableGet(temp),
+                    const DynamicType()))));
+      } else {
+        parts.add(spread);
+      }
+    } else if (spread == evaluator.nullConstant) {
+      // Null spread
+      if (!isNullAware) {
+        evaluator.report(spreadExpression, messageConstEvalNullValue);
+      }
+    } else {
+      // Fully evaluated spread
+      if (spread is MapConstant) {
+        for (ConstantMapEntry entry in spread.entries) {
+          addConstant(
+              entry.key, entry.value, spreadExpression, spreadExpression);
+        }
+      } else {
+        // Not map in spread
+        return evaluator.report(
+            spreadExpression, messageConstEvalNotMapInSpread);
+      }
+    }
+  }
+
+  void addConstant(Constant key, Constant value, TreeNode keyContext,
+      TreeNode valueContext) {
+    List<ConstantMapEntry> lastPart;
+    if (parts.last is List<ConstantMapEntry>) {
+      lastPart = parts.last;
+    } else {
+      parts.add(lastPart = <ConstantMapEntry>[]);
+    }
+    if (!evaluator.hasPrimitiveEqual(key)) {
+      evaluator.report(
+          keyContext, templateConstEvalKeyImplementsEqual.withArguments(key));
+    }
+    if (!seenKeys.add(key)) {
+      evaluator.report(
+          keyContext, templateConstEvalDuplicateKey.withArguments(key));
+    }
+    lastPart.add(new ConstantMapEntry(
+        evaluator.ensureIsSubtype(key, keyType, keyContext),
+        evaluator.ensureIsSubtype(value, valueType, valueContext)));
+  }
+
+  Constant build() {
+    if (parts.length == 1) {
+      // Fully evaluated
+      return evaluator
+          .lowerMapConstant(new MapConstant(keyType, valueType, parts.single));
+    }
+    List<Expression> maps = <Expression>[];
+    for (Object part in parts) {
+      if (part is List<ConstantMapEntry>) {
+        maps.add(
+            new ConstantExpression(new MapConstant(keyType, valueType, part)));
+      } else if (part is Constant) {
+        maps.add(evaluator.extract(part));
+      } else {
+        throw 'Non-constant in constant map';
+      }
+    }
+    return evaluator.unevaluated(original,
+        new MapConcatenation(maps, keyType: keyType, valueType: valueType));
+  }
+}
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 510b60b..d50ce8d 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -78,6 +78,8 @@
         IfMapEntry,
         SpreadMapEntry;
 
+part 'constant_collection_builders.dart';
+
 Component transformComponent(Component component, ConstantsBackend backend,
     Map<String, String> environmentDefines, ErrorReporter errorReporter,
     {bool keepFields: true,
@@ -765,220 +767,26 @@
     return canonicalize(result);
   }
 
-  /// Add an element (which is possibly a spread or an if element) to a
-  /// constant list or set represented as a list of (possibly unevaluated)
-  /// lists or sets to be concatenated.
-  /// Each element of [parts] is either a `List<Constant>` (containing fully
-  /// evaluated constants) or a `Constant` (potentially unevaluated).
-  /// Pass an identity set as [seen] for sets and omit it for lists.
-  void addToListOrSetConstant(
-      List<Object> parts, Expression element, DartType elementType,
-      [Set<Constant> seen]) {
-    bool isSet = seen != null;
-    if (element is SpreadElement) {
-      Constant spread = unlower(_evaluateSubexpression(element.expression));
-      if (shouldBeUnevaluated) {
-        // Unevaluated spread
-        if (element.isNullAware) {
-          VariableDeclaration temp =
-              new VariableDeclaration(null, initializer: extract(spread));
-          parts.add(unevaluated(
-              element.expression,
-              new Let(
-                  temp,
-                  new ConditionalExpression(
-                      new MethodInvocation(new VariableGet(temp),
-                          new Name('=='), new Arguments([new NullLiteral()])),
-                      new ListLiteral([], isConst: true),
-                      new VariableGet(temp),
-                      const DynamicType()))));
-        } else {
-          parts.add(spread);
-        }
-      } else if (spread == nullConstant) {
-        // Null spread
-        if (!element.isNullAware) {
-          report(element.expression, messageConstEvalNullValue);
-        }
-      } else {
-        // Fully evaluated spread
-        List<Constant> entries;
-        if (spread is ListConstant) {
-          entries = spread.entries;
-        } else if (spread is SetConstant) {
-          entries = spread.entries;
-        } else {
-          // Not list or set in spread
-          return report(
-              element.expression, messageConstEvalNotListOrSetInSpread);
-        }
-        for (Constant entry in entries) {
-          addToListOrSetConstant(
-              parts, new ConstantExpression(entry), elementType, seen);
-        }
-      }
-    } else if (element is IfElement) {
-      Constant condition = _evaluateSubexpression(element.condition);
-      if (shouldBeUnevaluated) {
-        // Unevaluated if
-        enterLazy();
-        Constant then = _evaluateSubexpression(isSet
-            ? new SetLiteral([cloner.clone(element.then)], isConst: true)
-            : new ListLiteral([cloner.clone(element.then)], isConst: true));
-        Constant otherwise;
-        if (element.otherwise != null) {
-          otherwise = _evaluateSubexpression(isSet
-              ? new SetLiteral([cloner.clone(element.otherwise)], isConst: true)
-              : new ListLiteral([cloner.clone(element.otherwise)],
-                  isConst: true));
-        } else {
-          otherwise = isSet
-              ? new SetConstant(const DynamicType(), [])
-              : new ListConstant(const DynamicType(), []);
-        }
-        leaveLazy();
-        parts.add(unevaluated(
-            element.condition,
-            new ConditionalExpression(extract(condition), extract(then),
-                extract(otherwise), const DynamicType())));
-      } else {
-        // Fully evaluated if
-        if (condition == trueConstant) {
-          addToListOrSetConstant(parts, element.then, elementType, seen);
-        } else if (condition == falseConstant) {
-          if (element.otherwise != null) {
-            addToListOrSetConstant(parts, element.otherwise, elementType, seen);
-          }
-        } else if (condition == nullConstant) {
-          report(element.condition, messageConstEvalNullValue);
-        } else {
-          report(
-              element.condition,
-              templateConstEvalInvalidType.withArguments(
-                  condition,
-                  typeEnvironment.boolType,
-                  condition.getType(typeEnvironment)));
-        }
-      }
-    } else if (element is ForElement || element is ForInElement) {
-      // For or for-in
-      report(
-          element,
-          isSet
-              ? messageConstEvalIterationInConstSet
-              : messageConstEvalIterationInConstList);
-    } else {
-      // Ordinary expresion element
-      Constant constant = _evaluateSubexpression(element);
-      if (shouldBeUnevaluated) {
-        parts.add(unevaluated(
-            element,
-            isSet
-                ? new SetLiteral([extract(constant)],
-                    typeArgument: elementType, isConst: true)
-                : new ListLiteral([extract(constant)],
-                    typeArgument: elementType, isConst: true)));
-      } else {
-        List<Constant> listOrSet;
-        if (parts.last is List<Constant>) {
-          listOrSet = parts.last;
-        } else {
-          parts.add(listOrSet = <Constant>[]);
-        }
-        if (isSet) {
-          if (!hasPrimitiveEqual(constant)) {
-            report(
-                element,
-                templateConstEvalElementImplementsEqual
-                    .withArguments(constant));
-          }
-          if (!seen.add(constant)) {
-            report(element,
-                templateConstEvalDuplicateElement.withArguments(constant));
-          }
-        }
-        listOrSet.add(ensureIsSubtype(constant, elementType, element));
-      }
-    }
-  }
-
-  Constant makeListConstantFromParts(
-      List<Object> parts, Expression node, DartType elementType) {
-    if (parts.length == 1) {
-      // Fully evaluated
-      return lowerListConstant(new ListConstant(elementType, parts.single));
-    }
-    List<Expression> lists = <Expression>[];
-    for (Object part in parts) {
-      if (part is List<Constant>) {
-        lists.add(new ConstantExpression(new ListConstant(elementType, part)));
-      } else if (part is Constant) {
-        lists.add(extract(part));
-      } else {
-        throw 'Non-constant in constant list';
-      }
-    }
-    return unevaluated(
-        node, new ListConcatenation(lists, typeArgument: elementType));
-  }
-
   visitListLiteral(ListLiteral node) {
     if (!node.isConst) {
       return report(
           node, templateConstEvalNonConstantLiteral.withArguments('List'));
     }
-    final List<Object> parts = <Object>[<Constant>[]];
+    final ListConstantBuilder builder =
+        new ListConstantBuilder(node, node.typeArgument, this);
     for (Expression element in node.expressions) {
-      addToListOrSetConstant(parts, element, node.typeArgument);
+      builder.add(element);
     }
-    return makeListConstantFromParts(parts, node, node.typeArgument);
+    return builder.build();
   }
 
   visitListConcatenation(ListConcatenation node) {
-    final List<Object> parts = <Object>[<Constant>[]];
+    final ListConstantBuilder builder =
+        new ListConstantBuilder(node, node.typeArgument, this);
     for (Expression list in node.lists) {
-      addToListOrSetConstant(parts,
-          new SpreadElement(cloner.clone(list), false), node.typeArgument);
+      builder.addSpread(list, isNullAware: false);
     }
-    return makeListConstantFromParts(parts, node, node.typeArgument);
-  }
-
-  Constant makeSetConstantFromParts(
-      List<Object> parts, Expression node, DartType elementType) {
-    if (parts.length == 1) {
-      // Fully evaluated
-      List<Constant> entries = parts.single;
-      SetConstant result = new SetConstant(elementType, entries);
-      if (desugarSets) {
-        final List<ConstantMapEntry> mapEntries =
-            new List<ConstantMapEntry>(entries.length);
-        for (int i = 0; i < entries.length; ++i) {
-          mapEntries[i] = new ConstantMapEntry(entries[i], nullConstant);
-        }
-        Constant map = lowerMapConstant(
-            new MapConstant(elementType, typeEnvironment.nullType, mapEntries));
-        return lower(
-            result,
-            new InstanceConstant(
-                unmodifiableSetMap.enclosingClass.reference,
-                [elementType],
-                <Reference, Constant>{unmodifiableSetMap.reference: map}));
-      } else {
-        return lowerSetConstant(result);
-      }
-    }
-    List<Expression> sets = <Expression>[];
-    for (Object part in parts) {
-      if (part is List<Constant>) {
-        sets.add(new ConstantExpression(new SetConstant(elementType, part)));
-      } else if (part is Constant) {
-        sets.add(extract(part));
-      } else {
-        throw 'Non-constant in constant set';
-      }
-    }
-    return unevaluated(
-        node, new SetConcatenation(sets, typeArgument: elementType));
+    return builder.build();
   }
 
   visitSetLiteral(SetLiteral node) {
@@ -986,169 +794,21 @@
       return report(
           node, templateConstEvalNonConstantLiteral.withArguments('Set'));
     }
-    final Set<Constant> seen = new Set<Constant>.identity();
-    final List<Object> parts = <Object>[<Constant>[]];
+    final SetConstantBuilder builder =
+        new SetConstantBuilder(node, node.typeArgument, this);
     for (Expression element in node.expressions) {
-      addToListOrSetConstant(parts, element, node.typeArgument, seen);
+      builder.add(element);
     }
-    return makeSetConstantFromParts(parts, node, node.typeArgument);
+    return builder.build();
   }
 
   visitSetConcatenation(SetConcatenation node) {
-    final Set<Constant> seen = new Set<Constant>.identity();
-    final List<Object> parts = <Object>[<Constant>[]];
+    final SetConstantBuilder builder =
+        new SetConstantBuilder(node, node.typeArgument, this);
     for (Expression set_ in node.sets) {
-      addToListOrSetConstant(
-          parts,
-          new SpreadElement(cloner.clone(set_), false),
-          node.typeArgument,
-          seen);
+      builder.addSpread(set_, isNullAware: false);
     }
-    return makeSetConstantFromParts(parts, node, node.typeArgument);
-  }
-
-  /// Add a map entry (which is possibly a spread or an if map entry) to a
-  /// constant map represented as a list of (possibly unevaluated)
-  /// maps to be concatenated.
-  /// Each element of [parts] is either a `List<ConstantMapEntry>` (containing
-  /// fully evaluated map entries) or a `Constant` (potentially unevaluated).
-  void addToMapConstant(List<Object> parts, MapEntry element, DartType keyType,
-      DartType valueType, Set<Constant> seenKeys) {
-    if (element is SpreadMapEntry) {
-      Constant spread = unlower(_evaluateSubexpression(element.expression));
-      if (shouldBeUnevaluated) {
-        // Unevaluated spread
-        if (element.isNullAware) {
-          VariableDeclaration temp =
-              new VariableDeclaration(null, initializer: extract(spread));
-          parts.add(unevaluated(
-              element.expression,
-              new Let(
-                  temp,
-                  new ConditionalExpression(
-                      new MethodInvocation(new VariableGet(temp),
-                          new Name('=='), new Arguments([new NullLiteral()])),
-                      new MapLiteral([], isConst: true),
-                      new VariableGet(temp),
-                      const DynamicType()))));
-        } else {
-          parts.add(spread);
-        }
-      } else if (spread == nullConstant) {
-        // Null spread
-        if (!element.isNullAware) {
-          report(element.expression, messageConstEvalNullValue);
-        }
-      } else {
-        // Fully evaluated spread
-        if (spread is MapConstant) {
-          for (ConstantMapEntry entry in spread.entries) {
-            addToMapConstant(
-                parts,
-                new MapEntry(new ConstantExpression(entry.key),
-                    new ConstantExpression(entry.value)),
-                keyType,
-                valueType,
-                seenKeys);
-          }
-        } else {
-          // Not map in spread
-          return report(element.expression, messageConstEvalNotMapInSpread);
-        }
-      }
-    } else if (element is IfMapEntry) {
-      Constant condition = _evaluateSubexpression(element.condition);
-      if (shouldBeUnevaluated) {
-        // Unevaluated if
-        enterLazy();
-        Constant then = _evaluateSubexpression(
-            new MapLiteral([cloner.clone(element.then)], isConst: true));
-        Constant otherwise;
-        if (element.otherwise != null) {
-          otherwise = _evaluateSubexpression(
-              new MapLiteral([cloner.clone(element.otherwise)], isConst: true));
-        } else {
-          otherwise =
-              new MapConstant(const DynamicType(), const DynamicType(), []);
-        }
-        leaveLazy();
-        parts.add(unevaluated(
-            element.condition,
-            new ConditionalExpression(extract(condition), extract(then),
-                extract(otherwise), const DynamicType())));
-      } else {
-        // Fully evaluated if
-        if (condition == trueConstant) {
-          addToMapConstant(parts, element.then, keyType, valueType, seenKeys);
-        } else if (condition == falseConstant) {
-          if (element.otherwise != null) {
-            addToMapConstant(
-                parts, element.otherwise, keyType, valueType, seenKeys);
-          }
-        } else if (condition == nullConstant) {
-          report(element.condition, messageConstEvalNullValue);
-        } else {
-          report(
-              element.condition,
-              templateConstEvalInvalidType.withArguments(
-                  condition,
-                  typeEnvironment.boolType,
-                  condition.getType(typeEnvironment)));
-        }
-      }
-    } else if (element is ForMapEntry || element is ForInMapEntry) {
-      // For or for-in
-      report(element, messageConstEvalIterationInConstMap);
-    } else {
-      // Ordinary map entry
-      Constant key = _evaluateSubexpression(element.key);
-      Constant value = _evaluateSubexpression(element.value);
-      if (shouldBeUnevaluated) {
-        parts.add(unevaluated(
-            element.key,
-            new MapLiteral([new MapEntry(extract(key), extract(value))],
-                isConst: true)));
-      } else {
-        List<ConstantMapEntry> entries;
-        if (parts.last is List<ConstantMapEntry>) {
-          entries = parts.last;
-        } else {
-          parts.add(entries = <ConstantMapEntry>[]);
-        }
-        if (!hasPrimitiveEqual(key)) {
-          report(
-              element, templateConstEvalKeyImplementsEqual.withArguments(key));
-        }
-        if (!seenKeys.add(key)) {
-          report(element.key, templateConstEvalDuplicateKey.withArguments(key));
-        }
-        entries.add(new ConstantMapEntry(
-            ensureIsSubtype(key, keyType, element.key),
-            ensureIsSubtype(value, valueType, element.value)));
-      }
-    }
-  }
-
-  Constant makeMapConstantFromParts(List<Object> parts, Expression node,
-      DartType keyType, DartType valueType) {
-    if (parts.length == 1) {
-      // Fully evaluated
-      return lowerMapConstant(
-          new MapConstant(keyType, valueType, parts.single));
-    }
-    List<Expression> maps = <Expression>[];
-    for (Object part in parts) {
-      if (part is List<ConstantMapEntry>) {
-        maps.add(
-            new ConstantExpression(new MapConstant(keyType, valueType, part)));
-      } else if (part is Constant) {
-        maps.add(extract(part));
-      } else {
-        throw 'Non-constant in constant map';
-      }
-    }
-    return unevaluated(node,
-        new MapConcatenation(maps, keyType: keyType, valueType: valueType));
+    return builder.build();
   }
 
   visitMapLiteral(MapLiteral node) {
@@ -1156,22 +816,21 @@
       return report(
           node, templateConstEvalNonConstantLiteral.withArguments('Map'));
     }
-    final Set<Constant> seen = new Set<Constant>.identity();
-    final List<Object> parts = <Object>[<ConstantMapEntry>[]];
+    final MapConstantBuilder builder =
+        new MapConstantBuilder(node, node.keyType, node.valueType, this);
     for (MapEntry element in node.entries) {
-      addToMapConstant(parts, element, node.keyType, node.valueType, seen);
+      builder.add(element);
     }
-    return makeMapConstantFromParts(parts, node, node.keyType, node.valueType);
+    return builder.build();
   }
 
   visitMapConcatenation(MapConcatenation node) {
-    final Set<Constant> seen = new Set<Constant>.identity();
-    final List<Object> parts = <Object>[<ConstantMapEntry>[]];
+    final MapConstantBuilder builder =
+        new MapConstantBuilder(node, node.keyType, node.valueType, this);
     for (Expression map in node.maps) {
-      addToMapConstant(parts, new SpreadMapEntry(cloner.clone(map), false),
-          node.keyType, node.valueType, seen);
+      builder.addSpread(map, isNullAware: false);
     }
-    return makeMapConstantFromParts(parts, node, node.keyType, node.valueType);
+    return builder.build();
   }
 
   visitFunctionExpression(FunctionExpression node) {