From 2da3806823458ea1f847679dbcecae17480dcfea Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Fri, 26 Aug 2022 17:10:41 +0000 Subject: [PATCH] [cfe] Handle Record(Index/Name)Get Change-Id: I9550d1e73b7caf1924596b3208a16f51a8cfcf41 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256362 Commit-Queue: Johnni Winther Reviewed-by: Chloe Stefantsova --- .../type_inference/inference_visitor.dart | 26 ++++++++++++++++++ .../testcases/records/record_get.dart | 11 ++++++++ .../records/record_get.dart.strong.expect | 11 ++++++++ .../record_get.dart.strong.transformed.expect | 11 ++++++++ .../record_get.dart.textual_outline.expect | 1 + ...d_get.dart.textual_outline_modelled.expect | 1 + .../records/record_get.dart.weak.expect | 11 ++++++++ .../record_get.dart.weak.modular.expect | 11 ++++++++ .../record_get.dart.weak.outline.expect | 5 ++++ .../record_get.dart.weak.transformed.expect | 11 ++++++++ .../testcases/records/record_get_errors.dart | 8 ++++++ .../record_get_errors.dart.strong.expect | 27 +++++++++++++++++++ ..._get_errors.dart.strong.transformed.expect | 27 +++++++++++++++++++ ...ord_get_errors.dart.textual_outline.expect | 1 + ...rrors.dart.textual_outline_modelled.expect | 1 + .../record_get_errors.dart.weak.expect | 27 +++++++++++++++++++ ...record_get_errors.dart.weak.modular.expect | 27 +++++++++++++++++++ ...record_get_errors.dart.weak.outline.expect | 5 ++++ ...rd_get_errors.dart.weak.transformed.expect | 27 +++++++++++++++++++ pkg/kernel/lib/ast.dart | 12 ++++++--- pkg/kernel/lib/binary/ast_from_binary.dart | 2 +- pkg/kernel/lib/text/ast_to_text.dart | 21 +++++++++++++++ 22 files changed, 279 insertions(+), 5 deletions(-) create mode 100644 pkg/front_end/testcases/records/record_get.dart create mode 100644 pkg/front_end/testcases/records/record_get.dart.strong.expect create mode 100644 pkg/front_end/testcases/records/record_get.dart.strong.transformed.expect create mode 100644 pkg/front_end/testcases/records/record_get.dart.textual_outline.expect create mode 100644 pkg/front_end/testcases/records/record_get.dart.textual_outline_modelled.expect create mode 100644 pkg/front_end/testcases/records/record_get.dart.weak.expect create mode 100644 pkg/front_end/testcases/records/record_get.dart.weak.modular.expect create mode 100644 pkg/front_end/testcases/records/record_get.dart.weak.outline.expect create mode 100644 pkg/front_end/testcases/records/record_get.dart.weak.transformed.expect create mode 100644 pkg/front_end/testcases/records/record_get_errors.dart create mode 100644 pkg/front_end/testcases/records/record_get_errors.dart.strong.expect create mode 100644 pkg/front_end/testcases/records/record_get_errors.dart.strong.transformed.expect create mode 100644 pkg/front_end/testcases/records/record_get_errors.dart.textual_outline.expect create mode 100644 pkg/front_end/testcases/records/record_get_errors.dart.textual_outline_modelled.expect create mode 100644 pkg/front_end/testcases/records/record_get_errors.dart.weak.expect create mode 100644 pkg/front_end/testcases/records/record_get_errors.dart.weak.modular.expect create mode 100644 pkg/front_end/testcases/records/record_get_errors.dart.weak.outline.expect create mode 100644 pkg/front_end/testcases/records/record_get_errors.dart.weak.transformed.expect diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart index cf1710fc83a..48b60645105 100644 --- a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart +++ b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart @@ -6021,6 +6021,32 @@ class InferenceVisitorImpl extends InferenceVisitorBase DartType receiverType = result.nullAwareActionType; node.receiver = receiver..parent = node; + + if (receiverType is RecordType) { + // TODO(johnniwinther): Handle nullable record types and null shorting. + String name = node.name.text; + if (name.startsWith('\$')) { + int? index = int.tryParse(name.substring(1)); + if (index != null) { + if (index < receiverType.positional.length) { + DartType fieldType = receiverType.positional[index]; + return new ExpressionInferenceResult( + fieldType, + new RecordIndexGet(receiver, receiverType, index) + ..fileOffset = node.fileOffset); + } + } + } + for (NamedType field in receiverType.named) { + if (field.name == name) { + return new ExpressionInferenceResult( + field.type, + new RecordNameGet(receiver, receiverType, name) + ..fileOffset = node.fileOffset); + } + } + } + PropertyGetInferenceResult propertyGetInferenceResult = _computePropertyGet( node.fileOffset, receiver, receiverType, node.name, typeContext, isThisReceiver: node.receiver is ThisExpression); diff --git a/pkg/front_end/testcases/records/record_get.dart b/pkg/front_end/testcases/records/record_get.dart new file mode 100644 index 00000000000..05f3284266f --- /dev/null +++ b/pkg/front_end/testcases/records/record_get.dart @@ -0,0 +1,11 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +method() { + (0, "").$0; + (0, "").$1; + (0, a: "", b: true).$0; + (0, a: "", b: true).a; + (0, a: "", b: true).b; +} \ No newline at end of file diff --git a/pkg/front_end/testcases/records/record_get.dart.strong.expect b/pkg/front_end/testcases/records/record_get.dart.strong.expect new file mode 100644 index 00000000000..c8f630477be --- /dev/null +++ b/pkg/front_end/testcases/records/record_get.dart.strong.expect @@ -0,0 +1,11 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +static method method() → dynamic { + (0, "").$0{core::int}; + (0, "").$1{core::String}; + (0, {a: "", b: true}).$0{core::int}; + (0, {a: "", b: true}).a{core::String}; + (0, {a: "", b: true}).b{core::bool}; +} diff --git a/pkg/front_end/testcases/records/record_get.dart.strong.transformed.expect b/pkg/front_end/testcases/records/record_get.dart.strong.transformed.expect new file mode 100644 index 00000000000..c8f630477be --- /dev/null +++ b/pkg/front_end/testcases/records/record_get.dart.strong.transformed.expect @@ -0,0 +1,11 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +static method method() → dynamic { + (0, "").$0{core::int}; + (0, "").$1{core::String}; + (0, {a: "", b: true}).$0{core::int}; + (0, {a: "", b: true}).a{core::String}; + (0, {a: "", b: true}).b{core::bool}; +} diff --git a/pkg/front_end/testcases/records/record_get.dart.textual_outline.expect b/pkg/front_end/testcases/records/record_get.dart.textual_outline.expect new file mode 100644 index 00000000000..83d2302aec4 --- /dev/null +++ b/pkg/front_end/testcases/records/record_get.dart.textual_outline.expect @@ -0,0 +1 @@ +method() {} diff --git a/pkg/front_end/testcases/records/record_get.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/records/record_get.dart.textual_outline_modelled.expect new file mode 100644 index 00000000000..83d2302aec4 --- /dev/null +++ b/pkg/front_end/testcases/records/record_get.dart.textual_outline_modelled.expect @@ -0,0 +1 @@ +method() {} diff --git a/pkg/front_end/testcases/records/record_get.dart.weak.expect b/pkg/front_end/testcases/records/record_get.dart.weak.expect new file mode 100644 index 00000000000..c8f630477be --- /dev/null +++ b/pkg/front_end/testcases/records/record_get.dart.weak.expect @@ -0,0 +1,11 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +static method method() → dynamic { + (0, "").$0{core::int}; + (0, "").$1{core::String}; + (0, {a: "", b: true}).$0{core::int}; + (0, {a: "", b: true}).a{core::String}; + (0, {a: "", b: true}).b{core::bool}; +} diff --git a/pkg/front_end/testcases/records/record_get.dart.weak.modular.expect b/pkg/front_end/testcases/records/record_get.dart.weak.modular.expect new file mode 100644 index 00000000000..c8f630477be --- /dev/null +++ b/pkg/front_end/testcases/records/record_get.dart.weak.modular.expect @@ -0,0 +1,11 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +static method method() → dynamic { + (0, "").$0{core::int}; + (0, "").$1{core::String}; + (0, {a: "", b: true}).$0{core::int}; + (0, {a: "", b: true}).a{core::String}; + (0, {a: "", b: true}).b{core::bool}; +} diff --git a/pkg/front_end/testcases/records/record_get.dart.weak.outline.expect b/pkg/front_end/testcases/records/record_get.dart.weak.outline.expect new file mode 100644 index 00000000000..befb402840d --- /dev/null +++ b/pkg/front_end/testcases/records/record_get.dart.weak.outline.expect @@ -0,0 +1,5 @@ +library /*isNonNullableByDefault*/; +import self as self; + +static method method() → dynamic + ; diff --git a/pkg/front_end/testcases/records/record_get.dart.weak.transformed.expect b/pkg/front_end/testcases/records/record_get.dart.weak.transformed.expect new file mode 100644 index 00000000000..c8f630477be --- /dev/null +++ b/pkg/front_end/testcases/records/record_get.dart.weak.transformed.expect @@ -0,0 +1,11 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +static method method() → dynamic { + (0, "").$0{core::int}; + (0, "").$1{core::String}; + (0, {a: "", b: true}).$0{core::int}; + (0, {a: "", b: true}).a{core::String}; + (0, {a: "", b: true}).b{core::bool}; +} diff --git a/pkg/front_end/testcases/records/record_get_errors.dart b/pkg/front_end/testcases/records/record_get_errors.dart new file mode 100644 index 00000000000..a0b88f56cdf --- /dev/null +++ b/pkg/front_end/testcases/records/record_get_errors.dart @@ -0,0 +1,8 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +method() { + (0, "").$2; + (0, a: "", b: true).c; +} \ No newline at end of file diff --git a/pkg/front_end/testcases/records/record_get_errors.dart.strong.expect b/pkg/front_end/testcases/records/record_get_errors.dart.strong.expect new file mode 100644 index 00000000000..5b3afb34c2d --- /dev/null +++ b/pkg/front_end/testcases/records/record_get_errors.dart.strong.expect @@ -0,0 +1,27 @@ +library /*isNonNullableByDefault*/; +// +// Problems in library: +// +// pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '$2' isn't defined for the class '(int, String)'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named '$2'. +// (0, "").$2; +// ^^ +// +// pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'. +// (0, a: "", b: true).c; +// ^ +// +import self as self; +import "dart:core" as core; + +static method method() → dynamic { + invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '\$2' isn't defined for the class '(int, String)'. +Try correcting the name to the name of an existing getter, or defining a getter or field named '\$2'. + (0, \"\").\$2; + ^^" in (0, ""){}.$2; + invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'. +Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'. + (0, a: \"\", b: true).c; + ^" in (0, {a: "", b: true}){}.c; +} diff --git a/pkg/front_end/testcases/records/record_get_errors.dart.strong.transformed.expect b/pkg/front_end/testcases/records/record_get_errors.dart.strong.transformed.expect new file mode 100644 index 00000000000..5b3afb34c2d --- /dev/null +++ b/pkg/front_end/testcases/records/record_get_errors.dart.strong.transformed.expect @@ -0,0 +1,27 @@ +library /*isNonNullableByDefault*/; +// +// Problems in library: +// +// pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '$2' isn't defined for the class '(int, String)'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named '$2'. +// (0, "").$2; +// ^^ +// +// pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'. +// (0, a: "", b: true).c; +// ^ +// +import self as self; +import "dart:core" as core; + +static method method() → dynamic { + invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '\$2' isn't defined for the class '(int, String)'. +Try correcting the name to the name of an existing getter, or defining a getter or field named '\$2'. + (0, \"\").\$2; + ^^" in (0, ""){}.$2; + invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'. +Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'. + (0, a: \"\", b: true).c; + ^" in (0, {a: "", b: true}){}.c; +} diff --git a/pkg/front_end/testcases/records/record_get_errors.dart.textual_outline.expect b/pkg/front_end/testcases/records/record_get_errors.dart.textual_outline.expect new file mode 100644 index 00000000000..83d2302aec4 --- /dev/null +++ b/pkg/front_end/testcases/records/record_get_errors.dart.textual_outline.expect @@ -0,0 +1 @@ +method() {} diff --git a/pkg/front_end/testcases/records/record_get_errors.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/records/record_get_errors.dart.textual_outline_modelled.expect new file mode 100644 index 00000000000..83d2302aec4 --- /dev/null +++ b/pkg/front_end/testcases/records/record_get_errors.dart.textual_outline_modelled.expect @@ -0,0 +1 @@ +method() {} diff --git a/pkg/front_end/testcases/records/record_get_errors.dart.weak.expect b/pkg/front_end/testcases/records/record_get_errors.dart.weak.expect new file mode 100644 index 00000000000..5b3afb34c2d --- /dev/null +++ b/pkg/front_end/testcases/records/record_get_errors.dart.weak.expect @@ -0,0 +1,27 @@ +library /*isNonNullableByDefault*/; +// +// Problems in library: +// +// pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '$2' isn't defined for the class '(int, String)'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named '$2'. +// (0, "").$2; +// ^^ +// +// pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'. +// (0, a: "", b: true).c; +// ^ +// +import self as self; +import "dart:core" as core; + +static method method() → dynamic { + invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '\$2' isn't defined for the class '(int, String)'. +Try correcting the name to the name of an existing getter, or defining a getter or field named '\$2'. + (0, \"\").\$2; + ^^" in (0, ""){}.$2; + invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'. +Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'. + (0, a: \"\", b: true).c; + ^" in (0, {a: "", b: true}){}.c; +} diff --git a/pkg/front_end/testcases/records/record_get_errors.dart.weak.modular.expect b/pkg/front_end/testcases/records/record_get_errors.dart.weak.modular.expect new file mode 100644 index 00000000000..5b3afb34c2d --- /dev/null +++ b/pkg/front_end/testcases/records/record_get_errors.dart.weak.modular.expect @@ -0,0 +1,27 @@ +library /*isNonNullableByDefault*/; +// +// Problems in library: +// +// pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '$2' isn't defined for the class '(int, String)'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named '$2'. +// (0, "").$2; +// ^^ +// +// pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'. +// (0, a: "", b: true).c; +// ^ +// +import self as self; +import "dart:core" as core; + +static method method() → dynamic { + invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '\$2' isn't defined for the class '(int, String)'. +Try correcting the name to the name of an existing getter, or defining a getter or field named '\$2'. + (0, \"\").\$2; + ^^" in (0, ""){}.$2; + invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'. +Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'. + (0, a: \"\", b: true).c; + ^" in (0, {a: "", b: true}){}.c; +} diff --git a/pkg/front_end/testcases/records/record_get_errors.dart.weak.outline.expect b/pkg/front_end/testcases/records/record_get_errors.dart.weak.outline.expect new file mode 100644 index 00000000000..befb402840d --- /dev/null +++ b/pkg/front_end/testcases/records/record_get_errors.dart.weak.outline.expect @@ -0,0 +1,5 @@ +library /*isNonNullableByDefault*/; +import self as self; + +static method method() → dynamic + ; diff --git a/pkg/front_end/testcases/records/record_get_errors.dart.weak.transformed.expect b/pkg/front_end/testcases/records/record_get_errors.dart.weak.transformed.expect new file mode 100644 index 00000000000..5b3afb34c2d --- /dev/null +++ b/pkg/front_end/testcases/records/record_get_errors.dart.weak.transformed.expect @@ -0,0 +1,27 @@ +library /*isNonNullableByDefault*/; +// +// Problems in library: +// +// pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '$2' isn't defined for the class '(int, String)'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named '$2'. +// (0, "").$2; +// ^^ +// +// pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'. +// (0, a: "", b: true).c; +// ^ +// +import self as self; +import "dart:core" as core; + +static method method() → dynamic { + invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '\$2' isn't defined for the class '(int, String)'. +Try correcting the name to the name of an existing getter, or defining a getter or field named '\$2'. + (0, \"\").\$2; + ^^" in (0, ""){}.$2; + invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'. +Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'. + (0, a: \"\", b: true).c; + ^" in (0, {a: "", b: true}){}.c; +} diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart index e1f07fe5ad5..bde22eb1063 100644 --- a/pkg/kernel/lib/ast.dart +++ b/pkg/kernel/lib/ast.dart @@ -4288,7 +4288,9 @@ class RecordIndexGet extends Expression { RecordType receiverType; final int index; - RecordIndexGet(this.receiver, this.receiverType, this.index); + RecordIndexGet(this.receiver, this.receiverType, this.index) { + receiver.parent = this; + } @override DartType getStaticType(StaticTypeContext context) => @@ -4330,7 +4332,7 @@ class RecordIndexGet extends Expression { @override void toTextInternal(AstPrinter printer) { printer.writeExpression(receiver); - printer.write("[${index}]"); + printer.write(".\$${index}"); } } @@ -4339,7 +4341,9 @@ class RecordNameGet extends Expression { RecordType receiverType; final String name; - RecordNameGet(this.receiver, this.receiverType, this.name); + RecordNameGet(this.receiver, this.receiverType, this.name) { + receiver.parent = this; + } @override DartType getStaticType(StaticTypeContext context) => @@ -4391,7 +4395,7 @@ class RecordNameGet extends Expression { @override void toTextInternal(AstPrinter printer) { printer.writeExpression(receiver); - printer.write("['${name}']"); + printer.write(".${name}"); } } diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart index dd93d6d7ecc..ce5eb5af347 100644 --- a/pkg/kernel/lib/binary/ast_from_binary.dart +++ b/pkg/kernel/lib/binary/ast_from_binary.dart @@ -2234,7 +2234,7 @@ class BinaryBuilder { int offset = readOffset(); Expression receiver = readExpression(); RecordType receiverType = readDartType() as RecordType; - String name = readString(); + String name = readStringReference(); return RecordNameGet(receiver, receiverType, name)..fileOffset = offset; } diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart index ce9f6fc2056..aebbf73e1f8 100644 --- a/pkg/kernel/lib/text/ast_to_text.dart +++ b/pkg/kernel/lib/text/ast_to_text.dart @@ -2177,6 +2177,27 @@ class Printer extends Visitor with VisitorVoidMixin { state = WORD; } + @override + void visitRecordIndexGet(RecordIndexGet node) { + writeExpression(node.receiver, Precedence.PRIMARY); + writeSymbol('.\$${node.index}'); + writeSymbol('{'); + writeType(node.receiverType.positional[node.index]); + writeSymbol('}'); + } + + @override + void visitRecordNameGet(RecordNameGet node) { + writeExpression(node.receiver, Precedence.PRIMARY); + writeSymbol('.${node.name}'); + writeSymbol('{'); + // TODO(johnniwinther): Should we store the result type in the node? + writeType(node.receiverType.named + .singleWhere((element) => element.name == node.name) + .type); + writeSymbol('}'); + } + @override void visitExpressionStatement(ExpressionStatement node) { writeIndentation();