[kernel] Collection concatenation nodes.
These arise when the constant evaluator partially evaluates collections
containing spreads or control-flow constructs with unevaluated
subexpressions. They are removed by the final constant evaluation.
Change-Id: Icdd155c4805cbcefe6aa4b45c2f85ec258e7bd36
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/95760
Commit-Queue: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 6c5ccc6..60b576f 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -4775,6 +4775,24 @@
}
@override
+ visitListConcatenation(ListConcatenation node) {
+ // Only occurs inside unevaluated constants.
+ throw new UnsupportedError("List concatenation");
+ }
+
+ @override
+ visitSetConcatenation(SetConcatenation node) {
+ // Only occurs inside unevaluated constants.
+ throw new UnsupportedError("Set concatenation");
+ }
+
+ @override
+ visitMapConcatenation(MapConcatenation node) {
+ // Only occurs inside unevaluated constants.
+ throw new UnsupportedError("Map concatenation");
+ }
+
+ @override
visitIsExpression(IsExpression node) {
return _emitIsExpression(node.operand, node.type);
}
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 5fab418..613741e7 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -137,7 +137,7 @@
type ComponentFile {
UInt32 magic = 0x90ABCDEF;
- UInt32 formatVersion = 20;
+ UInt32 formatVersion = 21;
List<String> problemsAsJson; // Described in problems.md.
Library[] libraries;
UriSource sourceMap;
@@ -696,6 +696,28 @@
List<Expression> expressions;
}
+type ListConcatenation extends Expression {
+ Byte tag = 111;
+ FileOffset fileOffset;
+ DartType typeArgument;
+ List<Expression> lists;
+}
+
+type SetConcatenation extends Expression {
+ Byte tag = 112;
+ FileOffset fileOffset;
+ DartType typeArgument;
+ List<Expression> sets;
+}
+
+type MapConcatenation extends Expression {
+ Byte tag = 113;
+ FileOffset fileOffset;
+ DartType keyType;
+ DartType valueType;
+ List<Expression> maps;
+}
+
type IsExpression extends Expression {
Byte tag = 37;
FileOffset fileOffset;
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 0f9cfa4..2d7e3a1 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -3204,6 +3204,116 @@
}
}
+/// Concatenate lists into a single list.
+///
+/// If [lists] is empty then an empty list is returned.
+///
+/// These arise from spread and control-flow elements in const list literals
+/// containing unevaluated subexpressions. They only ever occur within
+/// unevaluated constants in constant expressions.
+class ListConcatenation extends Expression {
+ DartType typeArgument;
+ final List<Expression> lists;
+
+ ListConcatenation(this.lists, {this.typeArgument: const DynamicType()}) {
+ setParents(lists, this);
+ }
+
+ DartType getStaticType(TypeEnvironment types) {
+ return types.literalListType(typeArgument);
+ }
+
+ accept(ExpressionVisitor v) => v.visitListConcatenation(this);
+ accept1(ExpressionVisitor1 v, arg) => v.visitListConcatenation(this, arg);
+
+ visitChildren(Visitor v) {
+ typeArgument?.accept(v);
+ visitList(lists, v);
+ }
+
+ transformChildren(Transformer v) {
+ typeArgument = v.visitDartType(typeArgument);
+ transformList(lists, v, this);
+ }
+}
+
+/// Concatenate sets into a single set.
+///
+/// If [sets] is empty then an empty set is returned.
+///
+/// These arise from spread and control-flow elements in const set literals
+/// containing unevaluated subexpressions. They only ever occur within
+/// unevaluated constants in constant expressions.
+///
+/// Duplicated values in or across the sets will result in a compile-time error
+/// during constant evaluation.
+class SetConcatenation extends Expression {
+ DartType typeArgument;
+ final List<Expression> sets;
+
+ SetConcatenation(this.sets, {this.typeArgument: const DynamicType()}) {
+ setParents(sets, this);
+ }
+
+ DartType getStaticType(TypeEnvironment types) {
+ return types.literalSetType(typeArgument);
+ }
+
+ accept(ExpressionVisitor v) => v.visitSetConcatenation(this);
+ accept1(ExpressionVisitor1 v, arg) => v.visitSetConcatenation(this, arg);
+
+ visitChildren(Visitor v) {
+ typeArgument?.accept(v);
+ visitList(sets, v);
+ }
+
+ transformChildren(Transformer v) {
+ typeArgument = v.visitDartType(typeArgument);
+ transformList(sets, v, this);
+ }
+}
+
+/// Concatenate maps into a single map.
+///
+/// If [maps] is empty then an empty map is returned.
+///
+/// These arise from spread and control-flow elements in const map literals
+/// containing unevaluated subexpressions. They only ever occur within
+/// unevaluated constants in constant expressions.
+///
+/// Duplicated keys in or across the maps will result in a compile-time error
+/// during constant evaluation.
+class MapConcatenation extends Expression {
+ DartType keyType;
+ DartType valueType;
+ final List<Expression> maps;
+
+ MapConcatenation(this.maps,
+ {this.keyType: const DynamicType(),
+ this.valueType: const DynamicType()}) {
+ setParents(maps, this);
+ }
+
+ DartType getStaticType(TypeEnvironment types) {
+ return types.literalMapType(keyType, valueType);
+ }
+
+ accept(ExpressionVisitor v) => v.visitMapConcatenation(this);
+ accept1(ExpressionVisitor1 v, arg) => v.visitMapConcatenation(this, arg);
+
+ visitChildren(Visitor v) {
+ keyType?.accept(v);
+ valueType?.accept(v);
+ visitList(maps, v);
+ }
+
+ transformChildren(Transformer v) {
+ keyType = v.visitDartType(keyType);
+ valueType = v.visitDartType(valueType);
+ transformList(maps, v, this);
+ }
+}
+
/// Expression of form `x is T`.
class IsExpression extends Expression {
Expression operand;
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 29fc2cc..f5b7c33 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -1511,6 +1511,25 @@
int offset = readOffset();
return new StringConcatenation(readExpressionList())
..fileOffset = offset;
+ case Tag.ListConcatenation:
+ int offset = readOffset();
+ var typeArgument = readDartType();
+ return new ListConcatenation(readExpressionList(),
+ typeArgument: typeArgument)
+ ..fileOffset = offset;
+ case Tag.SetConcatenation:
+ int offset = readOffset();
+ var typeArgument = readDartType();
+ return new SetConcatenation(readExpressionList(),
+ typeArgument: typeArgument)
+ ..fileOffset = offset;
+ case Tag.MapConcatenation:
+ int offset = readOffset();
+ var keyType = readDartType();
+ var valueType = readDartType();
+ return new MapConcatenation(readExpressionList(),
+ keyType: keyType, valueType: valueType)
+ ..fileOffset = offset;
case Tag.IsExpression:
int offset = readOffset();
return new IsExpression(readExpression(), readDartType())
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 383e6ba..db91bfb 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -1466,6 +1466,31 @@
}
@override
+ void visitListConcatenation(ListConcatenation node) {
+ writeByte(Tag.ListConcatenation);
+ writeOffset(node.fileOffset);
+ writeNode(node.typeArgument);
+ writeNodeList(node.lists);
+ }
+
+ @override
+ void visitSetConcatenation(SetConcatenation node) {
+ writeByte(Tag.SetConcatenation);
+ writeOffset(node.fileOffset);
+ writeNode(node.typeArgument);
+ writeNodeList(node.sets);
+ }
+
+ @override
+ void visitMapConcatenation(MapConcatenation node) {
+ writeByte(Tag.MapConcatenation);
+ writeOffset(node.fileOffset);
+ writeNode(node.keyType);
+ writeNode(node.valueType);
+ writeNodeList(node.maps);
+ }
+
+ @override
void visitIsExpression(IsExpression node) {
writeByte(Tag.IsExpression);
writeOffset(node.fileOffset);
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index ff6e228..ba6cc4e 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -50,6 +50,9 @@
static const int LogicalExpression = 34;
static const int ConditionalExpression = 35;
static const int StringConcatenation = 36;
+ static const int ListConcatenation = 111;
+ static const int SetConcatenation = 112;
+ static const int MapConcatenation = 113;
static const int IsExpression = 37;
static const int AsExpression = 38;
static const int StringLiteral = 39;
@@ -123,6 +126,9 @@
/// 108 is occupied by [RedirectingFactoryConstructor] (member).
/// 109 is occupied by [SetLiteral] (expression).
/// 110 is occupied by [ConstSetLiteral] (expression).
+ /// 111 is occupied by [ListConcatenation] (expression).
+ /// 112 is occupied by [SetConcatenation] (expression).
+ /// 113 is occupied by [MapConcatenation] (expression).
static const int SpecializedTagHighBit = 0x80; // 10000000
static const int SpecializedTagMask = 0xF8; // 11111000
@@ -139,7 +145,7 @@
/// Internal version of kernel binary format.
/// Bump it when making incompatible changes in kernel binaries.
/// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
- static const int BinaryFormatVersion = 20;
+ static const int BinaryFormatVersion = 21;
}
abstract class ConstantTag {
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index a059436..4105a43 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -190,6 +190,21 @@
return new StringConcatenation(node.expressions.map(clone).toList());
}
+ visitListConcatenation(ListConcatenation node) {
+ return new ListConcatenation(node.lists.map(clone).toList(),
+ typeArgument: visitType(node.typeArgument));
+ }
+
+ visitSetConcatenation(SetConcatenation node) {
+ return new SetConcatenation(node.sets.map(clone).toList(),
+ typeArgument: visitType(node.typeArgument));
+ }
+
+ visitMapConcatenation(MapConcatenation node) {
+ return new MapConcatenation(node.maps.map(clone).toList(),
+ keyType: visitType(node.keyType), valueType: visitType(node.valueType));
+ }
+
visitIsExpression(IsExpression node) {
return new IsExpression(clone(node.operand), visitType(node.type));
}
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 94d2f55..5e7d228 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -1272,6 +1272,33 @@
state = WORD;
}
+ visitListConcatenation(ListConcatenation node) {
+ bool first = true;
+ for (Expression part in node.lists) {
+ if (!first) writeSpaced('+');
+ writeExpression(part);
+ first = false;
+ }
+ }
+
+ visitSetConcatenation(SetConcatenation node) {
+ bool first = true;
+ for (Expression part in node.sets) {
+ if (!first) writeSpaced('+');
+ writeExpression(part);
+ first = false;
+ }
+ }
+
+ visitMapConcatenation(MapConcatenation node) {
+ bool first = true;
+ for (Expression part in node.maps) {
+ if (!first) writeSpaced('+');
+ writeExpression(part);
+ first = false;
+ }
+ }
+
visitIsExpression(IsExpression node) {
writeExpression(node.operand, Precedence.BITWISE_OR);
writeSpaced('is');
diff --git a/pkg/kernel/lib/text/text_serialization_verifier.dart b/pkg/kernel/lib/text/text_serialization_verifier.dart
index 2625376..b9f2662 100644
--- a/pkg/kernel/lib/text/text_serialization_verifier.dart
+++ b/pkg/kernel/lib/text/text_serialization_verifier.dart
@@ -888,6 +888,24 @@
}
@override
+ void visitListConcatenation(ListConcatenation node) {
+ storeLastSeenUriAndOffset(node);
+ makeExpressionRoundTrip(node);
+ }
+
+ @override
+ void visitSetConcatenation(SetConcatenation node) {
+ storeLastSeenUriAndOffset(node);
+ makeExpressionRoundTrip(node);
+ }
+
+ @override
+ void visitMapConcatenation(MapConcatenation node) {
+ storeLastSeenUriAndOffset(node);
+ makeExpressionRoundTrip(node);
+ }
+
+ @override
void visitConditionalExpression(ConditionalExpression node) {
storeLastSeenUriAndOffset(node);
makeExpressionRoundTrip(node);
diff --git a/pkg/kernel/lib/type_checker.dart b/pkg/kernel/lib/type_checker.dart
index 56eb744..7f04bce 100644
--- a/pkg/kernel/lib/type_checker.dart
+++ b/pkg/kernel/lib/type_checker.dart
@@ -683,6 +683,36 @@
}
@override
+ DartType visitListConcatenation(ListConcatenation node) {
+ DartType type = environment.literalListType(node.typeArgument);
+ for (Expression part in node.lists) {
+ DartType partType = visitExpression(part);
+ checkAssignable(node, type, partType);
+ }
+ return type;
+ }
+
+ @override
+ DartType visitSetConcatenation(SetConcatenation node) {
+ DartType type = environment.literalSetType(node.typeArgument);
+ for (Expression part in node.sets) {
+ DartType partType = visitExpression(part);
+ checkAssignable(node, type, partType);
+ }
+ return type;
+ }
+
+ @override
+ DartType visitMapConcatenation(MapConcatenation node) {
+ DartType type = environment.literalMapType(node.keyType, node.valueType);
+ for (Expression part in node.maps) {
+ DartType partType = visitExpression(part);
+ checkAssignable(node, type, partType);
+ }
+ return type;
+ }
+
+ @override
DartType visitStringLiteral(StringLiteral node) {
return environment.stringType;
}
diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart
index 53ea440..0cecee1 100644
--- a/pkg/kernel/lib/visitor.dart
+++ b/pkg/kernel/lib/visitor.dart
@@ -38,6 +38,9 @@
defaultExpression(node);
R visitStringConcatenation(StringConcatenation node) =>
defaultExpression(node);
+ R visitListConcatenation(ListConcatenation node) => defaultExpression(node);
+ R visitSetConcatenation(SetConcatenation node) => defaultExpression(node);
+ R visitMapConcatenation(MapConcatenation node) => defaultExpression(node);
R visitIsExpression(IsExpression node) => defaultExpression(node);
R visitAsExpression(AsExpression node) => defaultExpression(node);
R visitSymbolLiteral(SymbolLiteral node) => defaultExpression(node);
@@ -161,6 +164,9 @@
defaultExpression(node);
R visitStringConcatenation(StringConcatenation node) =>
defaultExpression(node);
+ R visitListConcatenation(ListConcatenation node) => defaultExpression(node);
+ R visitSetConcatenation(SetConcatenation node) => defaultExpression(node);
+ R visitMapConcatenation(MapConcatenation node) => defaultExpression(node);
R visitIsExpression(IsExpression node) => defaultExpression(node);
R visitAsExpression(AsExpression node) => defaultExpression(node);
R visitSymbolLiteral(SymbolLiteral node) => defaultExpression(node);
@@ -496,6 +502,12 @@
defaultExpression(node, arg);
R visitStringConcatenation(StringConcatenation node, T arg) =>
defaultExpression(node, arg);
+ R visitListConcatenation(ListConcatenation node, T arg) =>
+ defaultExpression(node, arg);
+ R visitSetConcatenation(SetConcatenation node, T arg) =>
+ defaultExpression(node, arg);
+ R visitMapConcatenation(MapConcatenation node, T arg) =>
+ defaultExpression(node, arg);
R visitIsExpression(IsExpression node, T arg) => defaultExpression(node, arg);
R visitAsExpression(AsExpression node, T arg) => defaultExpression(node, arg);
R visitSymbolLiteral(SymbolLiteral node, T arg) =>
diff --git a/runtime/vm/compiler/frontend/constant_evaluator.cc b/runtime/vm/compiler/frontend/constant_evaluator.cc
index 6733249..44f3bbd 100644
--- a/runtime/vm/compiler/frontend/constant_evaluator.cc
+++ b/runtime/vm/compiler/frontend/constant_evaluator.cc
@@ -94,6 +94,14 @@
case kStringConcatenation:
EvaluateStringConcatenation();
break;
+ case kListConcatenation:
+ case kSetConcatenation:
+ case kMapConcatenation:
+ // These only occur inside unevaluated constants, so if we decide to
+ // remove support for late evaluation of environment constants from
+ // dill files in the VM, an implementation here will not be necessary.
+ UNIMPLEMENTED();
+ break;
case kSymbolLiteral:
EvaluateSymbolLiteral();
break;
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 7e77d3d..7a3404e 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -1299,6 +1299,13 @@
return BuildConditionalExpression(position);
case kStringConcatenation:
return BuildStringConcatenation(position);
+ case kListConcatenation:
+ case kSetConcatenation:
+ case kMapConcatenation:
+ // Collection concatenation operations are removed by the constant
+ // evaluator.
+ UNREACHABLE();
+ break;
case kIsExpression:
return BuildIsExpression(position);
case kAsExpression:
diff --git a/runtime/vm/compiler/frontend/kernel_fingerprints.cc b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
index 58675d6..c9cdd92 100644
--- a/runtime/vm/compiler/frontend/kernel_fingerprints.cc
+++ b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
@@ -447,6 +447,13 @@
ReadPosition(); // read position.
CalculateListOfExpressionsFingerprint(); // read list of expressions.
return;
+ case kListConcatenation:
+ case kSetConcatenation:
+ case kMapConcatenation:
+ // Collection concatenation operations are removed by the constant
+ // evaluator.
+ UNREACHABLE();
+ break;
case kIsExpression:
ReadPosition(); // read position.
CalculateExpressionFingerprint(); // read operand.
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index 6194c70..d0b79b9 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -2197,6 +2197,13 @@
ReadPosition(); // read position.
SkipListOfExpressions(); // read list of expressions.
return;
+ case kListConcatenation:
+ case kSetConcatenation:
+ case kMapConcatenation:
+ // Collection concatenation operations are removed by the constant
+ // evaluator.
+ UNREACHABLE();
+ break;
case kIsExpression:
ReadPosition(); // read position.
SkipExpression(); // read operand.
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 71c2e61..7ab81b7 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -758,6 +758,13 @@
}
return;
}
+ case kListConcatenation:
+ case kSetConcatenation:
+ case kMapConcatenation:
+ // Collection concatenation operations are removed by the constant
+ // evaluator.
+ UNREACHABLE();
+ break;
case kIsExpression:
helper_.ReadPosition(); // read position.
VisitExpression(); // read operand.
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 7129b5f..28e92be 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,7 +20,7 @@
// Both version numbers are inclusive.
static const uint32_t kMinSupportedKernelFormatVersion = 18;
-static const uint32_t kMaxSupportedKernelFormatVersion = 20;
+static const uint32_t kMaxSupportedKernelFormatVersion = 21;
// Keep in sync with package:kernel/lib/binary/tag.dart
#define KERNEL_TAG_LIST(V) \
@@ -62,6 +62,9 @@
V(LogicalExpression, 34) \
V(ConditionalExpression, 35) \
V(StringConcatenation, 36) \
+ V(ListConcatenation, 111) \
+ V(SetConcatenation, 112) \
+ V(MapConcatenation, 113) \
V(IsExpression, 37) \
V(AsExpression, 38) \
V(StringLiteral, 39) \