[kernel] Add isWildcard flag to VariableDeclaration.

Adds the `isWildcard` flag to variables. Will be using this for producing better errors.

TEST= Existing expectations tests for wildcards pass with new flag.
Bug: https://github.com/dart-lang/sdk/issues/55655
Change-Id: If2f7a5555e7cc26e84d1b1e63e4261c81a157d78
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/370062
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
This commit is contained in:
Kallen Tu 2024-06-10 19:33:39 +00:00 committed by Commit Queue
parent 172bbe09cd
commit 141bb5417b
38 changed files with 82 additions and 61 deletions

View file

@ -3842,7 +3842,8 @@ class BodyBuilder extends StackListenerImpl
isLate: isLate,
isRequired: isRequired,
hasDeclaredInitializer: initializer != null,
isStaticLate: isFinal && initializer == null)
isStaticLate: isFinal && initializer == null,
isWildcard: identifier.name == '_')
..fileOffset = identifier.nameOffset
..fileEqualsOffset = offsetForToken(equalsToken);
typeInferrer.assignedVariables.declare(variable);
@ -3900,8 +3901,9 @@ class BodyBuilder extends StackListenerImpl
push(variable);
// Avoid adding the local identifier to scope if it's a wildcard.
if (!(libraryFeatures.wildcardVariables.isEnabled &&
variable.name == '_')) {
// TODO(kallentu): Emit better error on lookup, rather than not adding it to
// the scope.
if (!(libraryFeatures.wildcardVariables.isEnabled && variable.isWildcard)) {
declareVariable(variable, scope);
}
}

View file

@ -1151,7 +1151,8 @@ class VariableDeclarationImpl extends VariableDeclaration {
bool isRequired = false,
bool isLowered = false,
bool isSynthesized = false,
this.isStaticLate = false})
this.isStaticLate = false,
bool isWildcard = false})
: isImplicitlyTyped = type == null,
isLocalFunction = isLocalFunction,
super(name,
@ -1165,7 +1166,8 @@ class VariableDeclarationImpl extends VariableDeclaration {
isRequired: isRequired,
isLowered: isLowered,
isSynthesized: isSynthesized,
hasDeclaredInitializer: hasDeclaredInitializer);
hasDeclaredInitializer: hasDeclaredInitializer,
isWildcard: isWildcard);
VariableDeclarationImpl.forEffect(Expression initializer)
: forSyntheticToken = false,

View file

@ -7,7 +7,7 @@ static method foo(core::int x, core::int y) → core::int {
return z.{core::int::<<}(4){(core::int) → core::int};
}
static method loop(core::List<dynamic> xs) → void {
core::int _ = xs.{core::List::length}{core::int};
wildcard core::int _ = xs.{core::List::length}{core::int};
for (core::int i = 0; i.{core::num::<}(xs.{core::List::length}{core::int}){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) {
}
}

View file

@ -7,7 +7,7 @@ static method foo(core::int x, core::int y) → core::int {
return z.{core::int::<<}(4){(core::int) → core::int};
}
static method loop(core::List<dynamic> xs) → void {
core::int _ = xs.{core::List::length}{core::int};
wildcard core::int _ = xs.{core::List::length}{core::int};
for (core::int i = 0; i.{core::num::<}(xs.{core::List::length}{core::int}){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) {
}
}

View file

@ -7,7 +7,7 @@ static method foo(core::int x, core::int y) → core::int {
return z.{core::int::<<}(4){(core::int) → core::int};
}
static method loop(core::List<dynamic> xs) → void {
core::int _ = xs.{core::List::length}{core::int};
wildcard core::int _ = xs.{core::List::length}{core::int};
for (core::int i = 0; i.{core::num::<}(xs.{core::List::length}{core::int}){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) {
}
}

View file

@ -14,7 +14,7 @@ class Bar extends core::Object {
;
}
static method useCallback(dynamic callback) → dynamic {
dynamic _ = callback{dynamic}();
wildcard dynamic _ = callback{dynamic}();
}
static method main() → dynamic {
dynamic x;
@ -23,5 +23,5 @@ static method main() → dynamic {
return new self::Foo::•();
}
self::useCallback(inner);
self::Bar _ = inner(){() → self::Foo}.{self::Foo::_field}{self::Bar};
wildcard self::Bar _ = inner(){() → self::Foo}.{self::Foo::_field}{self::Bar};
}

View file

@ -14,7 +14,7 @@ class Bar extends core::Object {
;
}
static method useCallback(dynamic callback) → dynamic {
dynamic _ = callback{dynamic}();
wildcard dynamic _ = callback{dynamic}();
}
static method main() → dynamic {
dynamic x;
@ -23,5 +23,5 @@ static method main() → dynamic {
return new self::Foo::•();
}
self::useCallback(inner);
self::Bar _ = inner(){() → self::Foo}.{self::Foo::_field}{self::Bar};
wildcard self::Bar _ = inner(){() → self::Foo}.{self::Foo::_field}{self::Bar};
}

View file

@ -14,7 +14,7 @@ class Bar extends core::Object {
;
}
static method useCallback(dynamic callback) → dynamic {
dynamic _ = callback{dynamic}();
wildcard dynamic _ = callback{dynamic}();
}
static method main() → dynamic {
dynamic x;
@ -23,5 +23,5 @@ static method main() → dynamic {
return new self::Foo::•();
}
self::useCallback(inner);
self::Bar _ = inner(){() → self::Foo}.{self::Foo::_field}{self::Bar};
wildcard self::Bar _ = inner(){() → self::Foo}.{self::Foo::_field}{self::Bar};
}

View file

@ -28,10 +28,10 @@ class X extends core::Object implements self::A, self::B {
;
}
static method useAsA(self::A object) → void {
dynamic _ = object.{self::A::field}{dynamic};
wildcard dynamic _ = object.{self::A::field}{dynamic};
}
static method useAsB(self::B object) → void {
dynamic _ = object.{self::B::field}{dynamic};
wildcard dynamic _ = object.{self::B::field}{dynamic};
self::escape(object);
}
static method escape(dynamic x) → void {

View file

@ -28,10 +28,10 @@ class X extends core::Object implements self::A, self::B {
;
}
static method useAsA(self::A object) → void {
dynamic _ = object.{self::A::field}{dynamic};
wildcard dynamic _ = object.{self::A::field}{dynamic};
}
static method useAsB(self::B object) → void {
dynamic _ = object.{self::B::field}{dynamic};
wildcard dynamic _ = object.{self::B::field}{dynamic};
self::escape(object);
}
static method escape(dynamic x) → void {

View file

@ -28,10 +28,10 @@ class X extends core::Object implements self::A, self::B {
;
}
static method useAsA(self::A object) → void {
dynamic _ = object.{self::A::field}{dynamic};
wildcard dynamic _ = object.{self::A::field}{dynamic};
}
static method useAsB(self::B object) → void {
dynamic _ = object.{self::B::field}{dynamic};
wildcard dynamic _ = object.{self::B::field}{dynamic};
self::escape(object);
}
static method escape(dynamic x) → void {

View file

@ -5,7 +5,7 @@ import "dart:async" as asy;
import "dart:async";
static method test(asy::StreamController<void> _eventStreamController) → dynamic async /* emittedValueType= dynamic */ {
await for (final void _ in _eventStreamController.{asy::StreamController::stream}{asy::Stream<void>}) {
await for (final wildcard void _ in _eventStreamController.{asy::StreamController::stream}{asy::Stream<void>}) {
}
}
static method main() → dynamic {}

View file

@ -5,7 +5,7 @@ import "dart:async" as asy;
import "dart:async";
static method test(asy::StreamController<void> _eventStreamController) → dynamic async /* emittedValueType= dynamic */ {
await for (final void _ in _eventStreamController.{asy::StreamController::stream}{asy::Stream<void>}) {
await for (final wildcard void _ in _eventStreamController.{asy::StreamController::stream}{asy::Stream<void>}) {
}
}
static method main() → dynamic {}

View file

@ -11,7 +11,7 @@ static method test(asy::StreamController<void> _eventStreamController) → dynam
synthesized asy::_StreamIterator<void>? :for-iterator = new asy::_StreamIterator::•<void>(:stream);
try
while (let dynamic #t1 = asy::_asyncStarMoveNextHelper(:stream) in await :for-iterator.{asy::_StreamIterator::moveNext}(){() → asy::Future<core::bool>}) {
final void _ = :for-iterator.{asy::_StreamIterator::current}{void};
final wildcard void _ = :for-iterator.{asy::_StreamIterator::current}{void};
{}
}
finally

View file

@ -6,7 +6,7 @@ static method main() → void {
final core::List<core::String> works = <core::String>["b", "l"];
final core::List<core::String> fails = <core::String>["c"];
for (final core::List<core::String> test in <core::List<core::String>>[works, fails]) {
final void _ = block {
final wildcard void _ = block {
void #t1;
final synthesized core::List<core::String> #0#0 = test;
synthesized core::int #0#1;

View file

@ -6,7 +6,7 @@ static method main() → void {
final core::List<core::String> works = <core::String>["b", "l"];
final core::List<core::String> fails = <core::String>["c"];
for (final core::List<core::String> test in <core::List<core::String>>[works, fails]) {
final void _ = block {
final wildcard void _ = block {
void #t1;
final synthesized core::List<core::String> #0#0 = test;
synthesized core::int #0#1;

View file

@ -10,7 +10,7 @@ static method main() → void {
for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
final core::List<core::String> test = :sync-for-iterator.{core::Iterator::current}{core::List<core::String>};
{
final void _ = block {
final wildcard void _ = block {
void #t1;
final synthesized core::List<core::String> #0#0 = test;
synthesized core::int #0#1;

View file

@ -4,7 +4,7 @@ import "dart:core" as core;
import "dart:_internal" as _in;
static method main() → void {
for ((core::Iterable<core::int>, core::Iterable<core::int>) _ in self::split<core::int>(<core::int>[1, 2, 3])) {
for (wildcard(core::Iterable<core::int>, core::Iterable<core::int>) _ in self::split<core::int>(<core::int>[1, 2, 3])) {
}
}
static method split<A extends core::Object? = dynamic>(core::Iterable<self::split::A%> it) → core::Iterable<(core::Iterable<self::split::A%>, core::Iterable<self::split::A%>)>

View file

@ -4,7 +4,7 @@ import "dart:core" as core;
import "dart:_internal" as _in;
static method main() → void {
for ((core::Iterable<core::int>, core::Iterable<core::int>) _ in self::split<core::int>(<core::int>[1, 2, 3])) {
for (wildcard(core::Iterable<core::int>, core::Iterable<core::int>) _ in self::split<core::int>(<core::int>[1, 2, 3])) {
}
}
static method split<A extends core::Object? = dynamic>(core::Iterable<self::split::A%> it) → core::Iterable<(core::Iterable<self::split::A%>, core::Iterable<self::split::A%>)>

View file

@ -7,7 +7,7 @@ static method main() → void {
{
synthesized core::Iterator<(core::Iterable<core::int>, core::Iterable<core::int>)> :sync-for-iterator = self::split<core::int>(core::_GrowableList::_literal3<core::int>(1, 2, 3)).{core::Iterable::iterator}{core::Iterator<(core::Iterable<core::int>, core::Iterable<core::int>)>};
for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
(core::Iterable<core::int>, core::Iterable<core::int>) _ = :sync-for-iterator.{core::Iterator::current}{(core::Iterable<core::int>, core::Iterable<core::int>)};
wildcard(core::Iterable<core::int>, core::Iterable<core::int>) _ = :sync-for-iterator.{core::Iterator::current}{(core::Iterable<core::int>, core::Iterable<core::int>)};
{}
}
}

View file

@ -19,19 +19,19 @@ import "dart:core" as core;
import "dart:async" as asy;
static method main() → dynamic async /* emittedValueType= dynamic */ {
for (core::int _ = 0, core::int _ = 2; ; ) {
for (wildcard core::int _ = 0, wildcard core::int _ = 2; ; ) {
core::print(invalid-expression "pkg/front_end/testcases/wildcard_variables/for_loop.dart:8:11: Error: Undefined name '_'.
print(_);
^");
}
core::List<dynamic> list = <dynamic>[];
for (dynamic _ in list) {
for (wildcard dynamic _ in list) {
core::print(invalid-expression "pkg/front_end/testcases/wildcard_variables/for_loop.dart:13:11: Error: Undefined name '_'.
print(_);
^");
}
asy::Stream<dynamic> stream = new asy::_EmptyStream::•<dynamic>();
await for (dynamic _ in stream) {
await for (wildcard dynamic _ in stream) {
core::print(invalid-expression "pkg/front_end/testcases/wildcard_variables/for_loop.dart:18:11: Error: Undefined name '_'.
print(_);
^");

View file

@ -19,19 +19,19 @@ import "dart:core" as core;
import "dart:async" as asy;
static method main() → dynamic async /* emittedValueType= dynamic */ {
for (core::int _ = 0, core::int _ = 2; ; ) {
for (wildcard core::int _ = 0, wildcard core::int _ = 2; ; ) {
core::print(invalid-expression "pkg/front_end/testcases/wildcard_variables/for_loop.dart:8:11: Error: Undefined name '_'.
print(_);
^");
}
core::List<dynamic> list = <dynamic>[];
for (dynamic _ in list) {
for (wildcard dynamic _ in list) {
core::print(invalid-expression "pkg/front_end/testcases/wildcard_variables/for_loop.dart:13:11: Error: Undefined name '_'.
print(_);
^");
}
asy::Stream<dynamic> stream = new asy::_EmptyStream::•<dynamic>();
await for (dynamic _ in stream) {
await for (wildcard dynamic _ in stream) {
core::print(invalid-expression "pkg/front_end/testcases/wildcard_variables/for_loop.dart:18:11: Error: Undefined name '_'.
print(_);
^");

View file

@ -19,7 +19,7 @@ import "dart:core" as core;
import "dart:async" as asy;
static method main() → dynamic async /* emittedValueType= dynamic */ {
for (core::int _ = 0, core::int _ = 2; ; ) {
for (wildcard core::int _ = 0, wildcard core::int _ = 2; ; ) {
core::print(invalid-expression "pkg/front_end/testcases/wildcard_variables/for_loop.dart:8:11: Error: Undefined name '_'.
print(_);
^");
@ -28,7 +28,7 @@ static method main() → dynamic async /* emittedValueType= dynamic */ {
{
synthesized core::Iterator<dynamic> :sync-for-iterator = list.{core::Iterable::iterator}{core::Iterator<dynamic>};
for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
dynamic _ = :sync-for-iterator.{core::Iterator::current}{dynamic};
wildcard dynamic _ = :sync-for-iterator.{core::Iterator::current}{dynamic};
{
core::print(invalid-expression "pkg/front_end/testcases/wildcard_variables/for_loop.dart:13:11: Error: Undefined name '_'.
print(_);
@ -42,7 +42,7 @@ static method main() → dynamic async /* emittedValueType= dynamic */ {
synthesized asy::_StreamIterator<dynamic>? :for-iterator = new asy::_StreamIterator::•<dynamic>(:stream);
try
while (let dynamic #t1 = asy::_asyncStarMoveNextHelper(:stream) in await :for-iterator.{asy::_StreamIterator::moveNext}(){() → asy::Future<core::bool>}) {
dynamic _ = :for-iterator.{asy::_StreamIterator::current}{dynamic};
wildcard dynamic _ = :for-iterator.{asy::_StreamIterator::current}{dynamic};
{
core::print(invalid-expression "pkg/front_end/testcases/wildcard_variables/for_loop.dart:18:11: Error: Undefined name '_'.
print(_);

View file

@ -8,8 +8,8 @@ class Field extends core::Object {
: super core::Object::•()
;
method member() → void {
core::int _ = 1;
core::int _ = 2;
wildcard core::int _ = 1;
wildcard core::int _ = 2;
this.{self::Field::_} = 3;
}
}

View file

@ -8,8 +8,8 @@ class Field extends core::Object {
: super core::Object::•()
;
method member() → void {
core::int _ = 1;
core::int _ = 2;
wildcard core::int _ = 1;
wildcard core::int _ = 2;
this.{self::Field::_} = 3;
}
}

View file

@ -8,8 +8,8 @@ class Field extends core::Object {
: super core::Object::•()
;
method member() → void {
core::int _ = 1;
core::int _ = 2;
wildcard core::int _ = 1;
wildcard core::int _ = 2;
this.{self::Field::_} = 3;
}
}

View file

@ -10,8 +10,8 @@ import self as self;
import "dart:core" as core;
static method main() → dynamic {
core::int _ = 1;
core::int _ = 2;
wildcard core::int _ = 1;
wildcard core::int _ = 2;
invalid-expression "pkg/front_end/testcases/wildcard_variables/local_var.dart:8:3: Error: Setter not found: '_'.
_ = 3;
^";

View file

@ -10,8 +10,8 @@ import self as self;
import "dart:core" as core;
static method main() → dynamic {
core::int _ = 1;
core::int _ = 2;
wildcard core::int _ = 1;
wildcard core::int _ = 2;
invalid-expression "pkg/front_end/testcases/wildcard_variables/local_var.dart:8:3: Error: Setter not found: '_'.
_ = 3;
^";

View file

@ -10,8 +10,8 @@ import self as self;
import "dart:core" as core;
static method main() → dynamic {
core::int _ = 1;
core::int _ = 2;
wildcard core::int _ = 1;
wildcard core::int _ = 2;
invalid-expression "pkg/front_end/testcases/wildcard_variables/local_var.dart:8:3: Error: Setter not found: '_'.
_ = 3;
^";

View file

@ -4,7 +4,7 @@ import "dart:core" as core;
static field core::int _ = 100;
static method main() → dynamic {
core::int _ = 1;
core::int _ = 2;
wildcard core::int _ = 1;
wildcard core::int _ = 2;
self::_ = 3;
}

View file

@ -4,7 +4,7 @@ import "dart:core" as core;
static field core::int _ = 100;
static method main() → dynamic {
core::int _ = 1;
core::int _ = 2;
wildcard core::int _ = 1;
wildcard core::int _ = 2;
self::_ = 3;
}

View file

@ -4,7 +4,7 @@ import "dart:core" as core;
static field core::int _ = 100;
static method main() → dynamic {
core::int _ = 1;
core::int _ = 2;
wildcard core::int _ = 1;
wildcard core::int _ = 2;
self::_ = 3;
}

View file

@ -147,7 +147,7 @@ type CanonicalName {
type ComponentFile {
UInt32 magic = 0x90ABCDEF;
UInt32 formatVersion = 118;
UInt32 formatVersion = 119;
Byte[10] shortSdkHash;
List<String> problemsAsJson; // Described in problems.md.
Library[] libraries;
@ -1501,7 +1501,7 @@ type VariableDeclarationPlain {
UInt flags (isFinal, isConst, hasDeclaredInitializer, isInitializingFormal,
isCovariantByClass, isLate, isRequired, isCovariantByDeclaration,
isLowered, isSynthesized, isHoisted);
isLowered, isSynthesized, isHoisted, isWildcard);
// For named parameters, this is the parameter name.
// For other variables, the name is cosmetic, may be empty,
// and is not necessarily unique.

View file

@ -10513,7 +10513,8 @@ class VariableDeclaration extends Statement implements Annotatable {
bool isLowered = false,
bool isSynthesized = false,
bool isHoisted = false,
bool hasDeclaredInitializer = false}) {
bool hasDeclaredInitializer = false,
bool isWildcard = false}) {
initializer?.parent = this;
if (flags != -1) {
this.flags = flags;
@ -10528,6 +10529,7 @@ class VariableDeclaration extends Statement implements Annotatable {
this.hasDeclaredInitializer = hasDeclaredInitializer;
this.isSynthesized = isSynthesized;
this.isHoisted = isHoisted;
this.isWildcard = isWildcard;
}
assert(_name != null || this.isSynthesized,
"Only synthesized variables can have no name.");
@ -10576,6 +10578,7 @@ class VariableDeclaration extends Statement implements Annotatable {
static const int FlagLowered = 1 << 8;
static const int FlagSynthesized = 1 << 9;
static const int FlagHoisted = 1 << 10;
static const int FlagWildcard = 1 << 11;
bool get isFinal => flags & FlagFinal != 0;
bool get isConst => flags & FlagConst != 0;
@ -10641,6 +10644,11 @@ class VariableDeclaration extends Statement implements Annotatable {
/// as the initializer of the second variable.
bool get hasDeclaredInitializer => flags & FlagHasDeclaredInitializer != 0;
/// Whether this variable is a wildcard variable.
///
/// Wildcard variables have the name `_`.
bool get isWildcard => flags & FlagWildcard != 0;
/// Whether the variable is assignable.
///
/// This is `true` if the variable is neither constant nor final, or if it
@ -10709,6 +10717,12 @@ class VariableDeclaration extends Statement implements Annotatable {
: (flags & ~FlagHasDeclaredInitializer);
}
void set isWildcard(bool value) {
// TODO(kallentu): Change the name to be unique with other wildcard
// variables.
flags = value ? (flags | FlagWildcard) : (flags & ~FlagWildcard);
}
void clearAnnotations() {
annotations = const <Expression>[];
}

View file

@ -226,7 +226,7 @@ class Tag {
/// 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 = 118;
static const int BinaryFormatVersion = 119;
}
abstract class ConstantTag {

View file

@ -2452,6 +2452,7 @@ class Printer extends VisitorDefault<void> with VisitorVoidMixin {
writeModifier(node.isConst, 'const');
writeModifier(node.isSynthesized && node.name != null, 'synthesized');
writeModifier(node.isHoisted, 'hoisted');
writeModifier(node.isWildcard, 'wildcard');
bool hasImplicitInitializer = node.initializer is NullLiteral ||
(node.initializer is ConstantExpression &&
(node.initializer as ConstantExpression).constant is NullConstant);

View file

@ -419,6 +419,7 @@ class VariableDeclarationHelper {
kLowered = 1 << 8,
kSynthesized = 1 << 9,
kHoisted = 1 << 10,
kWildcard = 1 << 11,
};
explicit VariableDeclarationHelper(KernelReaderHelper* helper)
@ -440,6 +441,7 @@ class VariableDeclarationHelper {
bool IsRequired() const { return (flags_ & kRequired) != 0; }
bool IsSynthesized() const { return (flags_ & kSynthesized) != 0; }
bool IsHoisted() const { return (flags_ & kHoisted) != 0; }
bool IsWildcard() const { return (flags_ & kWildcard) != 0; }
bool HasDeclaredInitializer() const {
return (flags_ & kHasDeclaredInitializer) != 0;
}

View file

@ -18,7 +18,7 @@ namespace kernel {
// package:kernel/binary.md.
static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
static const uint32_t kSupportedKernelFormatVersion = 118;
static const uint32_t kSupportedKernelFormatVersion = 119;
// Keep in sync with package:kernel/lib/binary/tag.dart
#define KERNEL_TAG_LIST(V) \