[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) \