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 {}