Reland "[cfe] Handle spread elements of static type Null"

This is a reland of 0a13ef7d03c79aafc18a9165b30ef885cfe81bbd

Original change's description:
> [cfe] Handle spread elements of static type Null
> 
> Change-Id: Idec027766e77237c25fe2274426f92e24f45dec4
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96043
> Reviewed-by: Kevin Millikin <kmillikin@google.com>

Change-Id: I222c2b153cdf0ed47a0e83aff5deca4686c10343
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96131
Reviewed-by: Régis Crelier <regis@google.com>
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index c12729c..053b2de 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -6921,6 +6921,14 @@
     message: r"""Can only use type variables in instance methods.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeNonNullAwareSpreadIsNull = messageNonNullAwareSpreadIsNull;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageNonNullAwareSpreadIsNull = const MessageCode(
+    "NonNullAwareSpreadIsNull",
+    message: r"""Can't spread a value with static type Null.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeNonPartOfDirectiveInPart = messageNonPartOfDirectiveInPart;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
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 a81d124..52e701e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -638,12 +638,15 @@
     inferrer.inferStatement(node.body);
   }
 
-  DartType getSpreadElementType(DartType spreadType) {
+  DartType getSpreadElementType(DartType spreadType, bool isNullAware) {
     if (spreadType is InterfaceType) {
       InterfaceType supertype = inferrer.typeSchemaEnvironment
           .getTypeAsInstanceOf(spreadType, inferrer.coreTypes.iterableClass);
-      if (supertype == null) return null;
-      return supertype.typeArguments[0];
+      if (supertype != null) return supertype.typeArguments[0];
+      if (spreadType.classNode == inferrer.coreTypes.nullClass && isNullAware) {
+        return spreadType;
+      }
+      return null;
     }
     if (spreadType is DynamicType) return const DynamicType();
     return null;
@@ -691,8 +694,9 @@
             spreadTypes[i] = spreadType;
           }
           // Use 'dynamic' for error recovery.
-          actualTypes
-              .add(getSpreadElementType(spreadType) ?? const DynamicType());
+          actualTypes.add(
+              getSpreadElementType(spreadType, judgment.isNullAware) ??
+                  const DynamicType());
         } else {
           inferrer.inferExpression(judgment, inferredTypeArgument,
               inferenceNeeded || typeChecksNeeded,
@@ -725,34 +729,39 @@
         Expression item = node.expressions[i];
         if (item is SpreadElement) {
           DartType spreadType = spreadTypes[i];
-          DartType spreadElementType = getSpreadElementType(spreadType);
+          DartType spreadElementType =
+              getSpreadElementType(spreadType, item.isNullAware);
           if (spreadElementType == null) {
-            node.replaceChild(
-                node.expressions[i],
-                inferrer.helper.desugarSyntheticExpression(inferrer.helper
-                    .buildProblem(
-                        templateSpreadTypeMismatch.withArguments(spreadType),
-                        item.expression.fileOffset,
-                        1)));
+            if (spreadType is InterfaceType &&
+                spreadType.classNode == inferrer.coreTypes.nullClass &&
+                !item.isNullAware) {
+              node.replaceChild(
+                  node.expressions[i],
+                  inferrer.helper.desugarSyntheticExpression(inferrer.helper
+                      .buildProblem(messageNonNullAwareSpreadIsNull,
+                          item.expression.fileOffset, 1)));
+            } else {
+              node.replaceChild(
+                  node.expressions[i],
+                  inferrer.helper.desugarSyntheticExpression(inferrer.helper
+                      .buildProblem(
+                          templateSpreadTypeMismatch.withArguments(spreadType),
+                          item.expression.fileOffset,
+                          1)));
+            }
           } else if (spreadType is DynamicType) {
             inferrer.ensureAssignable(inferrer.coreTypes.iterableClass.rawType,
                 spreadType, item.expression, item.expression.fileOffset);
           } else if (spreadType is InterfaceType) {
-            if (spreadType.classNode == inferrer.coreTypes.nullClass) {
-              // TODO(dmitryas):  Handle this case when null-aware spreads are
-              // supported by the parser.
-            } else {
-              if (!inferrer.isAssignable(
-                  node.typeArgument, spreadElementType)) {
-                node.replaceChild(
-                    node.expressions[i],
-                    inferrer.helper.desugarSyntheticExpression(inferrer.helper
-                        .buildProblem(
-                            templateSpreadElementTypeMismatch.withArguments(
-                                spreadElementType, node.typeArgument),
-                            item.expression.fileOffset,
-                            1)));
-              }
+            if (!inferrer.isAssignable(node.typeArgument, spreadElementType)) {
+              node.replaceChild(
+                  node.expressions[i],
+                  inferrer.helper.desugarSyntheticExpression(inferrer.helper
+                      .buildProblem(
+                          templateSpreadElementTypeMismatch.withArguments(
+                              spreadElementType, node.typeArgument),
+                          item.expression.fileOffset,
+                          1)));
             }
           }
         } else {
@@ -1344,8 +1353,9 @@
             spreadTypes[i] = spreadType;
           }
           // Use 'dynamic' for error recovery.
-          actualTypes
-              .add(getSpreadElementType(spreadType) ?? const DynamicType());
+          actualTypes.add(
+              getSpreadElementType(spreadType, judgment.isNullAware) ??
+                  const DynamicType());
         } else {
           inferrer.inferExpression(judgment, inferredTypeArgument,
               inferenceNeeded || typeChecksNeeded,
@@ -1378,34 +1388,39 @@
         Expression item = node.expressions[i];
         if (item is SpreadElement) {
           DartType spreadType = spreadTypes[i];
-          DartType spreadElementType = getSpreadElementType(spreadType);
+          DartType spreadElementType =
+              getSpreadElementType(spreadType, item.isNullAware);
           if (spreadElementType == null) {
-            node.replaceChild(
-                node.expressions[i],
-                inferrer.helper.desugarSyntheticExpression(inferrer.helper
-                    .buildProblem(
-                        templateSpreadTypeMismatch.withArguments(spreadType),
-                        item.expression.fileOffset,
-                        1)));
+            if (spreadType is InterfaceType &&
+                spreadType.classNode == inferrer.coreTypes.nullClass &&
+                !item.isNullAware) {
+              node.replaceChild(
+                  node.expressions[i],
+                  inferrer.helper.desugarSyntheticExpression(inferrer.helper
+                      .buildProblem(messageNonNullAwareSpreadIsNull,
+                          item.expression.fileOffset, 1)));
+            } else {
+              node.replaceChild(
+                  node.expressions[i],
+                  inferrer.helper.desugarSyntheticExpression(inferrer.helper
+                      .buildProblem(
+                          templateSpreadTypeMismatch.withArguments(spreadType),
+                          item.expression.fileOffset,
+                          1)));
+            }
           } else if (spreadType is DynamicType) {
             inferrer.ensureAssignable(inferrer.coreTypes.iterableClass.rawType,
                 spreadType, item.expression, item.expression.fileOffset);
           } else if (spreadType is InterfaceType) {
-            if (spreadType.classNode == inferrer.coreTypes.nullClass) {
-              // TODO(dmitryas):  Handle this case when null-aware spreads are
-              // supported by the parser.
-            } else {
-              if (!inferrer.isAssignable(
-                  node.typeArgument, spreadElementType)) {
-                node.replaceChild(
-                    node.expressions[i],
-                    inferrer.helper.desugarSyntheticExpression(inferrer.helper
-                        .buildProblem(
-                            templateSpreadElementTypeMismatch.withArguments(
-                                spreadElementType, node.typeArgument),
-                            item.expression.fileOffset,
-                            1)));
-              }
+            if (!inferrer.isAssignable(node.typeArgument, spreadElementType)) {
+              node.replaceChild(
+                  node.expressions[i],
+                  inferrer.helper.desugarSyntheticExpression(inferrer.helper
+                      .buildProblem(
+                          templateSpreadElementTypeMismatch.withArguments(
+                              spreadElementType, node.typeArgument),
+                          item.expression.fileOffset,
+                          1)));
             }
           }
         } else {
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
index 9ebcc33..e99c7ba 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
@@ -38,6 +38,7 @@
         LocatedMessage,
         messageCantDisambiguateAmbiguousInformation,
         messageCantDisambiguateNotEnoughInformation,
+        messageNonNullAwareSpreadIsNull,
         messageSpreadElement,
         messageSpreadMapElement,
         messageSwitchExpressionNotAssignableCause,
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 69520ef..cfa04f1 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -276,6 +276,8 @@
 NonConstConstructor/example: Fail
 NonConstFactory/example: Fail
 NonInstanceTypeVariableUse/example: Fail
+NonNullAwareSpreadIsNull/analyzerCode: Fail # There's no analyzer code for that error yet.
+NonNullAwareSpreadIsNull/script: Fail # Can't be tested until 'spread-collections' flag is on.
 NonPartOfDirectiveInPart/script1: Fail
 NotAConstantExpression/example: Fail
 NotAType/example: Fail
@@ -344,7 +346,7 @@
 SpreadElementTypeMismatch/script: Fail # Can't be tested until 'spread-collections' flag is on.
 SpreadMapEntryElementKeyTypeMismatch/script: Fail # Can't be tested until 'spread-collections' flag is on.
 SpreadMapEntryElementValueTypeMismatch/script: Fail # Can't be tested until 'spread-collections' flag is on.
-SpreadMapEntryTypeMismatch/analyzerCode: Fail # Can't be tested until 'spread-collections' flag is on.
+SpreadMapEntryTypeMismatch/analyzerCode: Fail # There's no analyzer code for that error yet.
 SpreadMapEntryTypeMismatch/script1: Fail # Can't be tested until 'spread-collections' flag is on.
 SpreadMapEntryTypeMismatch/script2: Fail # Can't be tested until 'spread-collections' flag is on.
 SpreadTypeMismatch/analyzerCode: Fail # There's no analyzer code for that error yet.
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index e713239..3c39a0a 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -3553,3 +3553,10 @@
 SpreadMapElement:
   template: "Map spread."
   severity: CONTEXT
+
+NonNullAwareSpreadIsNull:
+  template: "Can't spread a value with static type Null."
+  script: >
+    main() {
+      <int>[...null];
+    }
diff --git a/pkg/front_end/testcases/spread_collection_inference.dart b/pkg/front_end/testcases/spread_collection_inference.dart
index 8547fed..3a9201a 100644
--- a/pkg/front_end/testcases/spread_collection_inference.dart
+++ b/pkg/front_end/testcases/spread_collection_inference.dart
@@ -113,6 +113,14 @@
 
   Map<String, String> map61 = <String, String>{...
     /*@error=SpreadMapEntryElementValueTypeMismatch*/ mapSpread};
+
+  List<int> lhs70 = <int>[... /*@error=NonNullAwareSpreadIsNull*/ null];
+
+  Set<int> set70 = <int>{... /*@error=NonNullAwareSpreadIsNull*/ null};
+
+  List<int> lhs80 = <int>[...?null];
+
+  Set<int> set80 = <int>{...?null};
 }
 
 main() {}
diff --git a/pkg/front_end/testcases/spread_collection_inference.dart.legacy.expect b/pkg/front_end/testcases/spread_collection_inference.dart.legacy.expect
index a976974..99bbd14 100644
--- a/pkg/front_end/testcases/spread_collection_inference.dart.legacy.expect
+++ b/pkg/front_end/testcases/spread_collection_inference.dart.legacy.expect
@@ -234,6 +234,22 @@
 //     /*@error=SpreadMapEntryElementValueTypeMismatch*/ mapSpread};
 //                                                       ^
 //
+// pkg/front_end/testcases/spread_collection_inference.dart:117:27: Error: Unexpected token '...'.
+//   List<int> lhs70 = <int>[... /*@error=NonNullAwareSpreadIsNull*/ null];
+//                           ^^^
+//
+// pkg/front_end/testcases/spread_collection_inference.dart:119:26: Error: Unexpected token '...'.
+//   Set<int> set70 = <int>{... /*@error=NonNullAwareSpreadIsNull*/ null};
+//                          ^^^
+//
+// pkg/front_end/testcases/spread_collection_inference.dart:121:27: Error: Unexpected token '...?'.
+//   List<int> lhs80 = <int>[...?null];
+//                           ^^^^
+//
+// pkg/front_end/testcases/spread_collection_inference.dart:123:26: Error: Unexpected token '...?'.
+//   Set<int> set80 = <int>{...?null};
+//                          ^^^^
+//
 import self as self;
 import "dart:core" as core;
 
@@ -279,5 +295,9 @@
   core::Set<core::String> set60 = <core::String>{spread};
   core::Map<core::int, core::int> map60 = <core::int, core::int>{};
   core::Map<core::String, core::String> map61 = <core::String, core::String>{};
+  core::List<core::int> lhs70 = <core::int>[null];
+  core::Set<core::int> set70 = <core::int>{null};
+  core::List<core::int> lhs80 = <core::int>[null];
+  core::Set<core::int> set80 = <core::int>{null};
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/spread_collection_inference.dart.legacy.transformed.expect b/pkg/front_end/testcases/spread_collection_inference.dart.legacy.transformed.expect
index a976974..99bbd14 100644
--- a/pkg/front_end/testcases/spread_collection_inference.dart.legacy.transformed.expect
+++ b/pkg/front_end/testcases/spread_collection_inference.dart.legacy.transformed.expect
@@ -234,6 +234,22 @@
 //     /*@error=SpreadMapEntryElementValueTypeMismatch*/ mapSpread};
 //                                                       ^
 //
+// pkg/front_end/testcases/spread_collection_inference.dart:117:27: Error: Unexpected token '...'.
+//   List<int> lhs70 = <int>[... /*@error=NonNullAwareSpreadIsNull*/ null];
+//                           ^^^
+//
+// pkg/front_end/testcases/spread_collection_inference.dart:119:26: Error: Unexpected token '...'.
+//   Set<int> set70 = <int>{... /*@error=NonNullAwareSpreadIsNull*/ null};
+//                          ^^^
+//
+// pkg/front_end/testcases/spread_collection_inference.dart:121:27: Error: Unexpected token '...?'.
+//   List<int> lhs80 = <int>[...?null];
+//                           ^^^^
+//
+// pkg/front_end/testcases/spread_collection_inference.dart:123:26: Error: Unexpected token '...?'.
+//   Set<int> set80 = <int>{...?null};
+//                          ^^^^
+//
 import self as self;
 import "dart:core" as core;
 
@@ -279,5 +295,9 @@
   core::Set<core::String> set60 = <core::String>{spread};
   core::Map<core::int, core::int> map60 = <core::int, core::int>{};
   core::Map<core::String, core::String> map61 = <core::String, core::String>{};
+  core::List<core::int> lhs70 = <core::int>[null];
+  core::Set<core::int> set70 = <core::int>{null};
+  core::List<core::int> lhs80 = <core::int>[null];
+  core::Set<core::int> set80 = <core::int>{null};
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/spread_collection_inference.dart.strong.expect b/pkg/front_end/testcases/spread_collection_inference.dart.strong.expect
index a45078f..4f527a6 100644
--- a/pkg/front_end/testcases/spread_collection_inference.dart.strong.expect
+++ b/pkg/front_end/testcases/spread_collection_inference.dart.strong.expect
@@ -87,6 +87,14 @@
 //     /*@error=SpreadMapEntryElementValueTypeMismatch*/ mapSpread};
 //                                                       ^
 //
+// pkg/front_end/testcases/spread_collection_inference.dart:117:67: Error: Can't spread a value with static type Null.
+//   List<int> lhs70 = <int>[... /*@error=NonNullAwareSpreadIsNull*/ null];
+//                                                                   ^
+//
+// pkg/front_end/testcases/spread_collection_inference.dart:119:66: Error: Can't spread a value with static type Null.
+//   Set<int> set70 = <int>{... /*@error=NonNullAwareSpreadIsNull*/ null};
+//                                                                  ^
+//
 import self as self;
 import "dart:core" as core;
 import "dart:collection" as col;
@@ -286,5 +294,25 @@
   core::Map<core::String, core::String> map61 = <core::String, core::String>{null: invalid-expression "pkg/front_end/testcases/spread_collection_inference.dart:115:55: Error: Can't assign spread entry values of type 'int' to map entry values of type 'String'.
     /*@error=SpreadMapEntryElementValueTypeMismatch*/ mapSpread};
                                                       ^"};
+  core::List<core::int> lhs70 = <core::int>[invalid-expression "pkg/front_end/testcases/spread_collection_inference.dart:117:67: Error: Can't spread a value with static type Null.
+  List<int> lhs70 = <int>[... /*@error=NonNullAwareSpreadIsNull*/ null];
+                                                                  ^"];
+  core::Set<core::int> set70 = let final core::Set<core::int> #t62 = col::LinkedHashSet::•<core::int>() in let final dynamic #t63 = #t62.{core::Set::add}(invalid-expression "pkg/front_end/testcases/spread_collection_inference.dart:119:66: Error: Can't spread a value with static type Null.
+  Set<int> set70 = <int>{... /*@error=NonNullAwareSpreadIsNull*/ null};
+                                                                 ^") in #t62;
+  core::List<core::int> lhs80 = block {
+    final core::List<core::int> #t64 = <core::int>[];
+    final dynamic #t65 = null;
+    if(!#t65.{core::Object::==}(null))
+      for (final core::int #t66 in #t65)
+        #t64.{core::List::add}(#t66);
+  } =>#t64;
+  core::Set<core::int> set80 = block {
+    final core::Set<core::int> #t67 = col::LinkedHashSet::•<core::int>();
+    final dynamic #t68 = null;
+    if(!#t68.{core::Object::==}(null))
+      for (final core::int #t69 in #t68)
+        #t67.{core::Set::add}(#t69);
+  } =>#t67;
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/spread_collection_inference.dart.strong.transformed.expect b/pkg/front_end/testcases/spread_collection_inference.dart.strong.transformed.expect
index 0f75319..7b62bb5 100644
--- a/pkg/front_end/testcases/spread_collection_inference.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/spread_collection_inference.dart.strong.transformed.expect
@@ -87,6 +87,14 @@
 //     /*@error=SpreadMapEntryElementValueTypeMismatch*/ mapSpread};
 //                                                       ^
 //
+// pkg/front_end/testcases/spread_collection_inference.dart:117:67: Error: Can't spread a value with static type Null.
+//   List<int> lhs70 = <int>[... /*@error=NonNullAwareSpreadIsNull*/ null];
+//                                                                   ^
+//
+// pkg/front_end/testcases/spread_collection_inference.dart:119:66: Error: Can't spread a value with static type Null.
+//   Set<int> set70 = <int>{... /*@error=NonNullAwareSpreadIsNull*/ null};
+//                                                                  ^
+//
 import self as self;
 import "dart:core" as core;
 import "dart:collection" as col;
@@ -286,5 +294,25 @@
   core::Map<core::String, core::String> map61 = <core::String, core::String>{null: invalid-expression "pkg/front_end/testcases/spread_collection_inference.dart:115:55: Error: Can't assign spread entry values of type 'int' to map entry values of type 'String'.
     /*@error=SpreadMapEntryElementValueTypeMismatch*/ mapSpread};
                                                       ^"};
+  core::List<core::int> lhs70 = <core::int>[invalid-expression "pkg/front_end/testcases/spread_collection_inference.dart:117:67: Error: Can't spread a value with static type Null.
+  List<int> lhs70 = <int>[... /*@error=NonNullAwareSpreadIsNull*/ null];
+                                                                  ^"];
+  core::Set<core::int> set70 = let final core::Set<core::int> #t62 = col::LinkedHashSet::•<core::int>() in let final core::bool #t63 = #t62.{core::Set::add}(invalid-expression "pkg/front_end/testcases/spread_collection_inference.dart:119:66: Error: Can't spread a value with static type Null.
+  Set<int> set70 = <int>{... /*@error=NonNullAwareSpreadIsNull*/ null};
+                                                                 ^") in #t62;
+  core::List<core::int> lhs80 = block {
+    final core::List<core::int> #t64 = <core::int>[];
+    final dynamic #t65 = null;
+    if(!#t65.{core::Object::==}(null))
+      for (final core::int #t66 in #t65)
+        #t64.{core::List::add}(#t66);
+  } =>#t64;
+  core::Set<core::int> set80 = block {
+    final core::Set<core::int> #t67 = col::LinkedHashSet::•<core::int>();
+    final dynamic #t68 = null;
+    if(!#t68.{core::Object::==}(null))
+      for (final core::int #t69 in #t68)
+        #t67.{core::Set::add}(#t69);
+  } =>#t67;
 }
 static method main() → dynamic {}