Reland "[cfe] Compile null-aware spreads in non-const maps"

This is a reland of 3160b084d8032f313c4b6e6f681092eb5ba66973

Original change's description:
> [cfe] Compile null-aware spreads in non-const maps
> 
> Change-Id: I22ee0b8821835bf8f2417f5fb4433898d06e7c36
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96060
> Reviewed-by: Kevin Millikin <kmillikin@google.com>

Change-Id: I46198eb380a1813723376e894d5d32261b25dfcb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96132
Reviewed-by: Régis Crelier <regis@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 123e9f2..c72f97c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -2554,7 +2554,7 @@
         if (entry is MapEntry) {
           entries.add(entry);
         } else if (entry is SpreadElement) {
-          entries.add(new SpreadMapEntry(entry.expression));
+          entries.add(new SpreadMapEntry(entry.expression, entry.isNullAware));
         } else {
           addProblem(
             fasta.templateExpectedAfterButGot.withArguments(':'),
diff --git a/pkg/front_end/lib/src/fasta/kernel/collections.dart b/pkg/front_end/lib/src/fasta/kernel/collections.dart
index c2b4dcc..74f6789 100644
--- a/pkg/front_end/lib/src/fasta/kernel/collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/collections.dart
@@ -204,8 +204,9 @@
 /// A spread element in a map literal.
 class SpreadMapEntry extends TreeNode with _FakeMapEntryMixin {
   Expression expression;
+  bool isNullAware;
 
-  SpreadMapEntry(this.expression) {
+  SpreadMapEntry(this.expression, this.isNullAware) {
     expression?.parent = this;
   }
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
index 3c2bdb6..d900fac 100644
--- a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
@@ -237,11 +237,21 @@
     for (; i < node.entries.length; ++i) {
       MapEntry entry = node.entries[i];
       if (entry is SpreadMapEntry) {
+        Expression value = entry.expression.accept(this);
+        // Null-aware spreads require testing the subexpression's value.
+        VariableDeclaration temp;
+        if (entry.isNullAware) {
+          temp = new VariableDeclaration.forValue(value,
+              type: coreTypes.mapClass.rawType);
+          body.add(temp);
+          value = new VariableGet(temp);
+        }
+
         VariableDeclaration elt =
             new VariableDeclaration(null, type: mapEntryType, isFinal: true);
-        body.add(new ForInStatement(
+        Statement statement = new ForInStatement(
             elt,
-            entry.expression.accept(this),
+            value,
             new ExpressionStatement(new MethodInvocation(
                 new VariableGet(map),
                 new Name('[]='),
@@ -251,7 +261,19 @@
                   new PropertyGet(
                       new VariableGet(elt), new Name('value'), mapEntryValue)
                 ]),
-                mapPut))));
+                mapPut)));
+
+        if (entry.isNullAware) {
+          statement = new IfStatement(
+              new Not(new MethodInvocation(
+                  new VariableGet(temp),
+                  new Name('=='),
+                  new Arguments([new NullLiteral()]),
+                  objectEquals)),
+              statement,
+              null);
+        }
+        body.add(statement);
       } else {
         entry = entry.accept(this);
         body.add(new ExpressionStatement(new MethodInvocation(
diff --git a/pkg/front_end/testcases/spread_collection.dart.strong.expect b/pkg/front_end/testcases/spread_collection.dart.strong.expect
index a05138f..8ca66da 100644
--- a/pkg/front_end/testcases/spread_collection.dart.strong.expect
+++ b/pkg/front_end/testcases/spread_collection.dart.strong.expect
@@ -27,19 +27,21 @@
     #t5.{core::Map::[]=}(1, 1);
     for (final core::MapEntry<core::int, core::int> #t6 in <core::int, core::int>{2: 2})
       #t5.{core::Map::[]=}(#t6.{core::MapEntry::key}, #t6.{core::MapEntry::value});
-    for (final core::MapEntry<core::int, core::int> #t7 in <core::int, core::int>{3: 3})
-      #t5.{core::Map::[]=}(#t7.{core::MapEntry::key}, #t7.{core::MapEntry::value});
+    final core::Map<dynamic, dynamic> #t7 = <core::int, core::int>{3: 3};
+    if(!#t7.{core::Object::==}(null))
+      for (final core::MapEntry<core::int, core::int> #t8 in #t7)
+        #t5.{core::Map::[]=}(#t8.{core::MapEntry::key}, #t8.{core::MapEntry::value});
   } =>#t5;
   final core::Set<core::int> aSet = block {
-    final core::Set<core::int> #t8 = col::LinkedHashSet::•<core::int>();
-    #t8.{core::Set::add}(1);
-    for (final core::int #t9 in <core::int>[2])
-      #t8.{core::Set::add}(#t9);
-    final dynamic #t10 = <core::int>[3];
-    if(!#t10.{core::Object::==}(null))
-      for (final core::int #t11 in #t10)
-        #t8.{core::Set::add}(#t11);
-  } =>#t8;
+    final core::Set<core::int> #t9 = col::LinkedHashSet::•<core::int>();
+    #t9.{core::Set::add}(1);
+    for (final core::int #t10 in <core::int>[2])
+      #t9.{core::Set::add}(#t10);
+    final dynamic #t11 = <core::int>[3];
+    if(!#t11.{core::Object::==}(null))
+      for (final core::int #t12 in #t11)
+        #t9.{core::Set::add}(#t12);
+  } =>#t9;
   final dynamic aSetOrMap = invalid-expression "pkg/front_end/testcases/spread_collection.dart:9:21: Error: Not enough type information to disambiguate between literal set and literal map.
 Try providing type arguments for the literal explicitly to disambiguate it.
   final aSetOrMap = {...foo()};
diff --git a/pkg/front_end/testcases/spread_collection.dart.strong.transformed.expect b/pkg/front_end/testcases/spread_collection.dart.strong.transformed.expect
index a05138f..8ca66da 100644
--- a/pkg/front_end/testcases/spread_collection.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/spread_collection.dart.strong.transformed.expect
@@ -27,19 +27,21 @@
     #t5.{core::Map::[]=}(1, 1);
     for (final core::MapEntry<core::int, core::int> #t6 in <core::int, core::int>{2: 2})
       #t5.{core::Map::[]=}(#t6.{core::MapEntry::key}, #t6.{core::MapEntry::value});
-    for (final core::MapEntry<core::int, core::int> #t7 in <core::int, core::int>{3: 3})
-      #t5.{core::Map::[]=}(#t7.{core::MapEntry::key}, #t7.{core::MapEntry::value});
+    final core::Map<dynamic, dynamic> #t7 = <core::int, core::int>{3: 3};
+    if(!#t7.{core::Object::==}(null))
+      for (final core::MapEntry<core::int, core::int> #t8 in #t7)
+        #t5.{core::Map::[]=}(#t8.{core::MapEntry::key}, #t8.{core::MapEntry::value});
   } =>#t5;
   final core::Set<core::int> aSet = block {
-    final core::Set<core::int> #t8 = col::LinkedHashSet::•<core::int>();
-    #t8.{core::Set::add}(1);
-    for (final core::int #t9 in <core::int>[2])
-      #t8.{core::Set::add}(#t9);
-    final dynamic #t10 = <core::int>[3];
-    if(!#t10.{core::Object::==}(null))
-      for (final core::int #t11 in #t10)
-        #t8.{core::Set::add}(#t11);
-  } =>#t8;
+    final core::Set<core::int> #t9 = col::LinkedHashSet::•<core::int>();
+    #t9.{core::Set::add}(1);
+    for (final core::int #t10 in <core::int>[2])
+      #t9.{core::Set::add}(#t10);
+    final dynamic #t11 = <core::int>[3];
+    if(!#t11.{core::Object::==}(null))
+      for (final core::int #t12 in #t11)
+        #t9.{core::Set::add}(#t12);
+  } =>#t9;
   final dynamic aSetOrMap = invalid-expression "pkg/front_end/testcases/spread_collection.dart:9:21: Error: Not enough type information to disambiguate between literal set and literal map.
 Try providing type arguments for the literal explicitly to disambiguate it.
   final aSetOrMap = {...foo()};