Temporary desugaring of set literals.

When compiling for backends that have not yet implemented support for
the set literal kernel node, transform all such literals into either a
Set() creation with a sequence of add() calls (for non-const literals)
or a const map literal wrapped in a const _UnmodifiableSet object
(for const literals).

The transformation is only performed on bodies or field initializers
that contain at least one actual set literal (i.e. not including
ambiguous set/map literals that were resolved as map literals), so as
to minimize the overhead for code not containing any set literals.

Change-Id: I80318717f0e8481a73f7034e18beea6494fd44bc
Reviewed-on: https://dart-review.googlesource.com/c/87326
Commit-Queue: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 31701d0..d023620 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -109,6 +109,8 @@
         getRedirectionTarget,
         isRedirectingFactory;
 
+import 'transform_set_literals.dart' show SetLiteralTransformer;
+
 import 'type_algorithms.dart' show calculateBounds;
 
 import 'kernel_api.dart';
@@ -202,6 +204,10 @@
 
   int functionNestingLevel = 0;
 
+  // Set by type inference when a set literal is encountered that needs to be
+  // transformed because the backend target does not support set literals.
+  bool transformSetLiterals = false;
+
   Statement problemInLoopOrSwitch;
 
   Scope switchScope;
@@ -547,6 +553,12 @@
           field.initializer = initializer;
           _typeInferrer?.inferFieldInitializer(
               this, field.builtType, initializer);
+
+          if (transformSetLiterals) {
+            library.loader.setLiteralTransformer ??=
+                new SetLiteralTransformer(library.loader);
+            field.target.accept(library.loader.setLiteralTransformer);
+          }
         }
       }
     }
@@ -736,6 +748,12 @@
     _typeInferrer?.inferFunctionBody(
         this, _computeReturnTypeContext(member), asyncModifier, body);
 
+    if (transformSetLiterals) {
+      library.loader.setLiteralTransformer ??=
+          new SetLiteralTransformer(library.loader);
+      body.accept(library.loader.setLiteralTransformer);
+    }
+
     // For async, async*, and sync* functions with declared return types, we
     // need to determine whether those types are valid.
     // TODO(hillerstrom): currently, we need to check whether [legacyMode] is
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index da03c43..08834e4 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -1114,6 +1114,12 @@
           node, inferrer.typeSchemaEnvironment,
           inferred: true);
     }
+
+    KernelLibraryBuilder library = inferrer.library;
+    if (library != null &&
+        !library.loader.target.backendTarget.supportsSetLiterals) {
+      inferrer.helper.transformSetLiterals = true;
+    }
   }
 
   void visitStaticAssignmentJudgment(
diff --git a/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart b/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart
new file mode 100644
index 0000000..e32f179
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart
@@ -0,0 +1,122 @@
+// 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.
+
+library fasta.transform_set_literals;
+
+import 'dart:core' hide MapEntry;
+
+import 'package:kernel/ast.dart'
+    show
+        Arguments,
+        Class,
+        Constructor,
+        ConstructorInvocation,
+        DartType,
+        Expression,
+        InterfaceType,
+        Let,
+        Library,
+        MapEntry,
+        MapLiteral,
+        MethodInvocation,
+        Name,
+        NullLiteral,
+        Procedure,
+        SetLiteral,
+        StaticInvocation,
+        TreeNode,
+        VariableDeclaration,
+        VariableGet;
+
+import 'package:kernel/core_types.dart' show CoreTypes;
+
+import 'package:kernel/visitor.dart' show Transformer;
+
+import '../source/source_loader.dart' show SourceLoader;
+
+import 'redirecting_factory_body.dart' show RedirectingFactoryBody;
+
+// TODO(askesc): Delete this class when all backends support set literals.
+class SetLiteralTransformer extends Transformer {
+  final CoreTypes coreTypes;
+  final DartType nullType;
+  final Procedure setFactory;
+  final Procedure addMethod;
+  final Constructor unmodifiableSetConstructor;
+
+  static Procedure _findSetFactory(CoreTypes coreTypes) {
+    Procedure factory = coreTypes.index.getMember('dart:core', 'Set', '');
+    RedirectingFactoryBody body = factory?.function?.body;
+    return body?.target;
+  }
+
+  static Procedure _findAddMethod(CoreTypes coreTypes) {
+    return coreTypes.index.getMember('dart:core', 'Set', 'add');
+  }
+
+  static Constructor _findUnmodifiableSetConstructor(
+      SourceLoader<Library> loader) {
+    // We should not generally dig into libraries like this, and we should
+    // avoid dependencies on libraries other than the ones indexed by
+    // CoreTypes. This is a temporary solution until all backends have
+    // implemented support for set literals.
+    Uri collectionUri = Uri.parse("dart:collection");
+    Library collectionLibrary = loader.builders[collectionUri].target;
+    for (int i = 0; i < collectionLibrary.classes.length; i++) {
+      Class classNode = collectionLibrary.classes[i];
+      if (classNode.name == "_UnmodifiableSet") {
+        for (int j = 0; j < collectionLibrary.classes.length; j++) {
+          Constructor constructor = classNode.constructors[j];
+          if (constructor.name.name.isEmpty) {
+            return constructor;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  SetLiteralTransformer(SourceLoader<Library> loader)
+      : coreTypes = loader.coreTypes,
+        nullType = new InterfaceType(loader.coreTypes.nullClass, []),
+        setFactory = _findSetFactory(loader.coreTypes),
+        addMethod = _findAddMethod(loader.coreTypes),
+        unmodifiableSetConstructor = _findUnmodifiableSetConstructor(loader);
+
+  TreeNode visitSetLiteral(SetLiteral node) {
+    if (node.isConst) {
+      List<MapEntry> entries = new List<MapEntry>(node.expressions.length);
+      for (int i = 0; i < node.expressions.length; i++) {
+        // expression_i: null
+        Expression entry = node.expressions[i].accept(this);
+        entries[i] = new MapEntry(entry, new NullLiteral());
+      }
+      Expression mapExp = new MapLiteral(entries,
+          keyType: node.typeArgument, valueType: nullType, isConst: true);
+      return new ConstructorInvocation(unmodifiableSetConstructor,
+          new Arguments([mapExp], types: [node.typeArgument]),
+          isConst: true);
+    } else {
+      // Outermost declaration of let chain: Set<E> setVar = new Set<E>();
+      VariableDeclaration setVar = new VariableDeclaration.forValue(
+          new StaticInvocation(
+              setFactory, new Arguments([], types: [node.typeArgument])),
+          type: new InterfaceType(coreTypes.setClass, [node.typeArgument]));
+      // Innermost body of let chain: setVar
+      Expression setExp = new VariableGet(setVar);
+      for (int i = node.expressions.length - 1; i >= 0; i--) {
+        // let _ = setVar.add(expression) in rest
+        Expression entry = node.expressions[i].accept(this);
+        setExp = new Let(
+            new VariableDeclaration.forValue(new MethodInvocation(
+                new VariableGet(setVar),
+                new Name("add"),
+                new Arguments([entry]),
+                addMethod)),
+            setExp);
+      }
+      return new Let(setVar, setExp);
+    }
+  }
+}
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index e2a0bc8..9ddb35a 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -89,6 +89,8 @@
 
 import '../kernel/body_builder.dart' show BodyBuilder;
 
+import '../kernel/transform_set_literals.dart' show SetLiteralTransformer;
+
 import '../loader.dart' show Loader, untranslatableUriScheme;
 
 import '../parser/class_member_parser.dart' show ClassMemberParser;
@@ -139,6 +141,8 @@
 
   Instrumentation instrumentation;
 
+  SetLiteralTransformer setLiteralTransformer;
+
   SourceLoader(this.fileSystem, this.includeComments, KernelTarget target)
       : super(target);
 
diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart
index ff3b0d4..c690103 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart
@@ -13,6 +13,8 @@
 
   Uri get uri;
 
+  set transformSetLiterals(bool value);
+
   Expression buildProblem(Message message, int charOffset, int length,
       {List<LocatedMessage> context, bool suppressMessage});