From c2787e1cf45826277770f7ab664c5e67302a9b5e Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Mon, 11 May 2026 00:38:41 -0700 Subject: [PATCH] [cfe][Contexts] Flatten variables hierarchy This removes the LegacyVariableDeclaration and VariableInitializationBase classes to create a simpler hierarchy. This is done preparation for splitting variables from statements. TEST=existing Change-Id: If29c9eee1e3d8bed819ce53ed178c3efa3bffaea Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501961 Reviewed-by: Alexander Markov Commit-Queue: Johnni Winther Reviewed-by: Chloe Stefantsova --- pkg/dart2bytecode/lib/bytecode_generator.dart | 9 +- pkg/dart2bytecode/lib/local_vars.dart | 8 +- .../lib/src/kernel/body_builder.dart | 16 +- pkg/front_end/lib/src/kernel/collections.dart | 8 +- .../lib/src/kernel/constant_evaluator.dart | 2 +- .../lib/src/kernel/internal_ast.dart | 4 +- .../lib/src/kernel/internal_ast_helper.dart | 4 +- .../src/type_inference/inference_visitor.dart | 9 +- .../static_types/type_arguments_test.dart | 2 +- .../synthetic_variables.dart.strong.expect | 10 +- ...hetic_variables.dart.strong.modular.expect | 10 +- pkg/front_end/tool/ast_model.dart | 6 +- pkg/kernel/lib/src/ast/statements.dart | 309 +++++------------- pkg/kernel/lib/src/ast/variables.dart | 25 +- pkg/kernel/lib/src/equivalence.dart | 6 + pkg/kernel/lib/src/printer.dart | 4 +- pkg/kernel/lib/text/ast_to_text.dart | 15 +- pkg/kernel/lib/type_checker.dart | 10 +- pkg/kernel/lib/verifier.dart | 6 +- pkg/kernel/lib/visitor.dart | 12 - .../transformations/for_in_lowering.dart | 8 +- 21 files changed, 171 insertions(+), 312 deletions(-) diff --git a/pkg/dart2bytecode/lib/bytecode_generator.dart b/pkg/dart2bytecode/lib/bytecode_generator.dart index c28cf8da3bf..0bd74351554 100644 --- a/pkg/dart2bytecode/lib/bytecode_generator.dart +++ b/pkg/dart2bytecode/lib/bytecode_generator.dart @@ -4860,10 +4860,7 @@ class BytecodeGenerator extends RecursiveVisitor { finallyBlocks.remove(node); } - bool _skipVariableInitialization( - VariableInitializationBase v, - bool isCaptured, - ) { + bool _skipVariableInitialization(VariableDeclaration v, bool isCaptured) { // We can skip variable initialization if the variable is supposed to be // initialized to null and it's captured. This is because all the slots in // the capture context are implicitly initialized to null. @@ -4886,11 +4883,11 @@ class BytecodeGenerator extends RecursiveVisitor { } @override - void visitVariableInitialization(VariableInitializationBase node) { + void visitVariableInitialization(VariableInitialization node) { _handleVariableInitialization(node); } - void _handleVariableInitialization(VariableInitializationBase node) { + void _handleVariableInitialization(VariableDeclaration node) { if (!node.isConst) { final bool isCaptured = locals.isCaptured(node.variable); final initializer = node.initializer; diff --git a/pkg/dart2bytecode/lib/local_vars.dart b/pkg/dart2bytecode/lib/local_vars.dart index ab5b9f3b334..52953d4ff06 100644 --- a/pkg/dart2bytecode/lib/local_vars.dart +++ b/pkg/dart2bytecode/lib/local_vars.dart @@ -553,11 +553,11 @@ class _ScopeBuilder extends RecursiveVisitor { } @override - void visitVariableInitialization(VariableInitializationBase node) { + void visitVariableInitialization(VariableInitialization node) { _handleVariableInitialization(node); } - void _handleVariableInitialization(VariableInitializationBase node) { + void _handleVariableInitialization(VariableDeclaration node) { _declareVariable(node.variable); node.visitChildren(this); } @@ -1097,11 +1097,11 @@ class _Allocator extends RecursiveVisitor { } @override - void visitVariableInitialization(VariableInitializationBase node) { + void visitVariableInitialization(VariableInitialization node) { _handleVariableInitialization(node); } - void _handleVariableInitialization(VariableInitializationBase node) { + void _handleVariableInitialization(VariableDeclaration node) { _allocateVariable(node.variable); node.visitChildren(this); } diff --git a/pkg/front_end/lib/src/kernel/body_builder.dart b/pkg/front_end/lib/src/kernel/body_builder.dart index 97cea58769e..5a3091a1071 100644 --- a/pkg/front_end/lib/src/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/kernel/body_builder.dart @@ -3338,7 +3338,7 @@ class BodyBuilderImpl extends StackListenerImpl } pushNewLocalVariable(initializer, equalsToken: assignmentOperator); if (isLate) { - VariableInitializationBase node = peek() as VariableInitializationBase; + VariableDeclaration node = peek() as VariableDeclaration; // This is matched by the call to [beginNode] in // [beginVariableInitializer]. @@ -3397,7 +3397,7 @@ class BodyBuilderImpl extends StackListenerImpl name = createWildcardVariableName(wildcardVariableIndex); wildcardVariableIndex++; } - VariableInitializationBase variableInitialization; + VariableDeclaration variableInitialization; InternalVariable internalVariable; if (isClosureContextLoweringEnabled) { internalVariable = new InternalLocalVariable( @@ -3412,7 +3412,7 @@ class BodyBuilderImpl extends StackListenerImpl forSyntheticToken: identifier.token.isSynthetic, isImplicitlyTyped: currentLocalVariableType == null, ); - variableInitialization = new VariableInitializationBase( + variableInitialization = new VariableInitialization( variable: internalVariable.asExpressionVariable, initializer: initializer, hasDeclaredInitializer: initializer != null, @@ -3521,8 +3521,7 @@ class BodyBuilderImpl extends StackListenerImpl push(node); return; } - VariableInitializationBase variableInitialization = - node as VariableInitializationBase; + VariableDeclaration variableInitialization = node as VariableDeclaration; variableInitialization.fileOffset = nameToken.charOffset; push(variableInitialization); @@ -3578,8 +3577,7 @@ class BodyBuilderImpl extends StackListenerImpl push(node); return; } - VariableInitializationBase variableInitialization = - node as VariableInitializationBase; + VariableDeclaration variableInitialization = node as VariableDeclaration; if (annotations != null) { for (int i = 0; i < annotations.length; i++) { variableInitialization.addAnnotation(annotations[i]); @@ -3723,11 +3721,11 @@ class BodyBuilderImpl extends StackListenerImpl if (variableOrExpression is Generator) { variableOrExpression = variableOrExpression.buildForEffect(); } - if (variableOrExpression is VariableInitializationBase) { + if (variableOrExpression is VariableDeclaration) { // Late for loop variables are not supported. An error has already been // reported by the parser. variableOrExpression.isLate = false; - return [variableOrExpression as VariableDeclaration]; + return [variableOrExpression]; } else if (variableOrExpression is Expression) { VariableDeclaration variable = new VariableDeclarationImpl.forEffect( variableOrExpression, diff --git a/pkg/front_end/lib/src/kernel/collections.dart b/pkg/front_end/lib/src/kernel/collections.dart index 1d912f1f573..a9f18c852c8 100644 --- a/pkg/front_end/lib/src/kernel/collections.dart +++ b/pkg/front_end/lib/src/kernel/collections.dart @@ -221,7 +221,7 @@ class ForElement extends ControlFlowElement implements ForElementBase { // May be empty, but not null. @override - final List variableInitializations; + final List variableInitializations; @override List get variables => variableInitializations.cast(); @@ -461,7 +461,7 @@ class IfCaseElement extends ControlFlowElementImpl } abstract interface class ForElementBase implements AuxiliaryExpression { - List get variableInitializations; + List get variableInitializations; List get variables; @@ -480,7 +480,7 @@ class PatternForElement extends ControlFlowElementImpl // May be empty, but not null. @override - final List variableInitializations; + final List variableInitializations; @override List get variables => variableInitializations.cast(); @@ -497,7 +497,7 @@ class PatternForElement extends ControlFlowElementImpl PatternForElement({ required this.patternVariableDeclaration, required this.intermediateVariables, - required List variables, + required List variables, required this.condition, required this.updates, required this.body, diff --git a/pkg/front_end/lib/src/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/kernel/constant_evaluator.dart index e08b353a7ce..2a22cea1cc1 100644 --- a/pkg/front_end/lib/src/kernel/constant_evaluator.dart +++ b/pkg/front_end/lib/src/kernel/constant_evaluator.dart @@ -6120,7 +6120,7 @@ class StatementConstantEvaluator @override ExecutionStatus visitForStatement(ForStatement node) { - for (VariableInitializationBase variable in node.variables) { + for (VariableDeclaration variable in node.variables) { final ExecutionStatus status = variable.accept(this); if (status is! ProceedStatus) return status; } diff --git a/pkg/front_end/lib/src/kernel/internal_ast.dart b/pkg/front_end/lib/src/kernel/internal_ast.dart index d44dd0a4d6a..9ef4f9ac1c5 100644 --- a/pkg/front_end/lib/src/kernel/internal_ast.dart +++ b/pkg/front_end/lib/src/kernel/internal_ast.dart @@ -1637,11 +1637,11 @@ mixin DelegatingVariableMixin on InternalVariableMixin @override // Coverage-ignore(suite): Not run. - VariableInitializationBase? get variableInitialization => + VariableDeclaration? get variableInitialization => astVariable.variableInitialization; @override - void set variableInitialization(VariableInitializationBase? value) { + void set variableInitialization(VariableDeclaration? value) { astVariable.variableInitialization = value; } diff --git a/pkg/front_end/lib/src/kernel/internal_ast_helper.dart b/pkg/front_end/lib/src/kernel/internal_ast_helper.dart index 8516a856b16..d5a48da3b68 100644 --- a/pkg/front_end/lib/src/kernel/internal_ast_helper.dart +++ b/pkg/front_end/lib/src/kernel/internal_ast_helper.dart @@ -281,7 +281,7 @@ MapLiteralEntry createIfCaseMapEntry( ForElement createForElement( int fileOffset, - List variables, + List variables, Expression? condition, List updates, Expression body, @@ -294,7 +294,7 @@ PatternForElement createPatternForElement( int fileOffset, { required PatternVariableDeclaration patternVariableDeclaration, required List intermediateVariables, - required List variables, + required List variables, required Expression? condition, required List updates, required Expression body, diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/type_inference/inference_visitor.dart index 5d022dfe211..726901a4892 100644 --- a/pkg/front_end/lib/src/type_inference/inference_visitor.dart +++ b/pkg/front_end/lib/src/type_inference/inference_visitor.dart @@ -4403,14 +4403,13 @@ class InferenceVisitorImpl extends InferenceVisitorBase Map inferredConditionTypes, ) { // TODO(johnniwinther): Use _visitStatements instead. - List? variables; + List? variables; for ( int index = 0; index < element.variableInitializations.length; index++ ) { - VariableInitializationBase variable = - element.variableInitializations[index]; + VariableDeclaration variable = element.variableInitializations[index]; if (variable.name == null) { if (variable.initializer != null) { ExpressionInferenceResult initializerResult = inferExpression( @@ -16758,7 +16757,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase @override StatementInferenceResult visitVariableInitialization( - VariableInitializationBase node, + VariableInitialization node, ) { InternalVariable nodeVariable = node.variable as InternalVariable; StatementInferenceResult statementInferenceResult = @@ -16774,7 +16773,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase } StatementInferenceResult _inferInternalExpressionVariableDeclaration( - VariableInitializationBase node, + VariableDeclaration node, InternalVariable nodeVariable, ) { DartType declaredType = nodeVariable.isImplicitlyTyped diff --git a/pkg/front_end/test/static_types/type_arguments_test.dart b/pkg/front_end/test/static_types/type_arguments_test.dart index 3a802461e3e..8fc3bd834f4 100644 --- a/pkg/front_end/test/static_types/type_arguments_test.dart +++ b/pkg/front_end/test/static_types/type_arguments_test.dart @@ -70,7 +70,7 @@ class TypeArgumentsVisitor extends VerifyingAnalysis { uri: astUri, ); InterfaceType variableInitializationType = interface - .createInterfaceType('VariableInitializationBase', uri: astUri); + .createInterfaceType('VariableDeclaration', uri: astUri); DartType typeArgument = receiver.arguments.types.single; if (interface.isSubtypeOf(typeArgument, expressionType) && typeArgument != expressionType) { diff --git a/pkg/front_end/testcases/closure_context_lowering/synthetic_variables.dart.strong.expect b/pkg/front_end/testcases/closure_context_lowering/synthetic_variables.dart.strong.expect index c4617917529..2d4352ed503 100644 --- a/pkg/front_end/testcases/closure_context_lowering/synthetic_variables.dart.strong.expect +++ b/pkg/front_end/testcases/closure_context_lowering/synthetic_variables.dart.strong.expect @@ -6,9 +6,17 @@ static method test(positional-parameter list) → dynamic/* scope=[ #ctx1: not-captured VariableContext([ positional-parameter list; ]), +] */ /* scope=[ + #ctx2: not-captured VariableContext([ + local-variable s; + ]), ] */ { s := ""; - for (synthetic-variable #t1 in list) { + for /* scope=[ + #ctx3: not-captured VariableContext([ + SyntheticVariable + ]), + ] */ (synthetic-variable #t1 in list) { s = #t1; if(s.{core::String::isNotEmpty}{core::bool}) { return s; diff --git a/pkg/front_end/testcases/closure_context_lowering/synthetic_variables.dart.strong.modular.expect b/pkg/front_end/testcases/closure_context_lowering/synthetic_variables.dart.strong.modular.expect index c4617917529..2d4352ed503 100644 --- a/pkg/front_end/testcases/closure_context_lowering/synthetic_variables.dart.strong.modular.expect +++ b/pkg/front_end/testcases/closure_context_lowering/synthetic_variables.dart.strong.modular.expect @@ -6,9 +6,17 @@ static method test(positional-parameter list) → dynamic/* scope=[ #ctx1: not-captured VariableContext([ positional-parameter list; ]), +] */ /* scope=[ + #ctx2: not-captured VariableContext([ + local-variable s; + ]), ] */ { s := ""; - for (synthetic-variable #t1 in list) { + for /* scope=[ + #ctx3: not-captured VariableContext([ + SyntheticVariable + ]), + ] */ (synthetic-variable #t1 in list) { s = #t1; if(s.{core::String::isNotEmpty}{core::bool}) { return s; diff --git a/pkg/front_end/tool/ast_model.dart b/pkg/front_end/tool/ast_model.dart index 88b13339a99..a3d0a0e8ac5 100644 --- a/pkg/front_end/tool/ast_model.dart +++ b/pkg/front_end/tool/ast_model.dart @@ -31,7 +31,8 @@ Uri computePackageConfig(Uri repoDir) => /// nominality. For instance the name of a variable declaration is taking as /// defining its identity. const Map _declarativeClassesNames = const { - 'LegacyVariableDeclaration': 'name', + // TODO(johnniwinther): This should be [VariableDeclaration]. + 'VariableStatement': 'name', 'TypeParameter': 'name', 'StructuralParameter': 'name', 'LabeledStatement': null, @@ -763,8 +764,7 @@ Future deriveAstModel(Uri repoDir, {bool printDump = false}) async { "and a rule must therefore specify " "whether this constitutes declarative or referential use.", ); - } - if (!rule.isDeclaration!) { + } else if (!rule.isDeclaration!) { return new FieldType(type, AstFieldKind.use); } } diff --git a/pkg/kernel/lib/src/ast/statements.dart b/pkg/kernel/lib/src/ast/statements.dart index 74536fc99e6..09001d95923 100644 --- a/pkg/kernel/lib/src/ast/statements.dart +++ b/pkg/kernel/lib/src/ast/statements.dart @@ -589,7 +589,7 @@ class ForStatement extends Statement implements LoopStatement, ScopeProvider { @override void transformOrRemoveChildren(RemovingTransformer v) { - v.transformVariableInitializationList(variables, this); + v.transformVariableDeclarationList(variables, this); if (condition != null) { condition = v.transformOrRemoveExpression(condition!); condition?.parent = this; @@ -1458,188 +1458,6 @@ class YieldStatement extends Statement { } } -abstract interface class LegacyVariableDeclaration - implements - Annotatable, - Statement, - VariableDeclaration, - VariableInitializationBase { - /// The name of the variable as provided in the source code. - /// - /// The name of a variable can only be omitted if the variable is synthesized. - /// Otherwise, its name is as provided in the source code. - @override - abstract String? name; - - /// The declared or inferred type of the variable. - @override - abstract DartType type; - - /// For locals, this is the initial value. - /// For parameters, this is the default value. - /// - /// Should be null in other cases. - @override - abstract Expression? initializer; - - @override - abstract int flags; - - /// Whether the parameter is declared with the `covariant` keyword. - @override - abstract bool isCovariantByDeclaration; - - /// If this [LegacyVariableDeclaration] is a parameter of a method, indicates - /// whether the method implementation needs to contain a runtime type check to - /// deal with generic covariance. - /// - /// When `true`, runtime checks may need to be performed. - @override - abstract bool isCovariantByClass; - - /// Whether the variable is declared with the `const` keyword. - @override - abstract bool isConst; - - /// Whether the variable is declared with the `late` keyword. - /// - /// The `late` modifier is only supported on local variables and not on - /// parameters. - @override - abstract bool isLate; - - /// Whether the variable is declared with the `final` keyword. - @override - abstract bool isFinal; - - /// Whether the parameter is declared with the `required` keyword. - /// - /// The `required` modifier is only supported on named parameters and not on - /// positional parameters and local variables. - @override - abstract bool isRequired; - - /// Whether the variable is part of a lowering. - /// - /// If a variable is part of a lowering its name may be synthesized so that it - /// doesn't reflect the name used in the source code and might not have a - /// one-to-one correspondence with the variable in the source. - /// - /// Lowering is used for instance of encoding of 'this' in extension instance - /// members and encoding of late locals. - @override - abstract bool isLowered; - - /// Whether the declaration of this variable is has been moved to an earlier - /// source location. - /// - /// This is for instance the case for variables declared in a pattern, where - /// the lowering requires the variable to be declared before the expression - /// that performs that matching in which its initialization occurs. - @override - abstract bool isHoisted; - - /// Whether this variable is synthesized, that is, it is _not_ declared in - /// the source code. - /// - /// The name of a variable can only be omitted if the variable is synthesized. - /// Otherwise, its name is as provided in the source code. - @override - abstract bool isSynthesized; - - /// Whether the variable is assignable. - /// - /// This is `true` if the variable is neither constant nor final, or if it - /// is late final without an initializer. - @override - bool get isAssignable; - - /// Whether the variable is declared as an initializing formal parameter of - /// a constructor. - @informative - @override - abstract bool isInitializingFormal; - - /// Whether the variable is declared as a super initializing formal parameter - /// of a constructor. - @informative - @override - abstract bool isSuperInitializingFormal; - - @informative - @override - abstract bool isErroneouslyInitialized; - - /// Whether the variable has an initializer, either by declaration or copied - /// from an original declaration. - /// - /// Note that the variable might have a synthesized initializer expression, - /// so `hasDeclaredInitializer == false` doesn't imply `initializer == null`. - /// For instance, for duplicate variable names, an invalid expression is set - /// as the initializer of the second variable. - @override - abstract bool hasDeclaredInitializer; - - /// Whether this variable is a wildcard variable. - /// - /// Wildcard variables have the name `_`. - @override - abstract bool isWildcard; - - /// Offset of the equals sign in the source file it comes from. - /// - /// Valid values are from 0 and up, or -1 ([TreeNode.noOffset]) - /// if the equals sign offset is not available (e.g. if not initialized) - /// (this is the default if none is specifically set). - @override - abstract int fileEqualsOffset; - - /// Offset of the declaration, set and used when writing the binary. - @override - abstract int binaryOffsetNoTag; - - /// List of metadata annotations on the variable declaration. - /// - /// This defaults to an immutable empty list. Use [addAnnotation] to add - /// annotations if needed. - @override - abstract List annotations; - - @override - void clearAnnotations(); - - factory LegacyVariableDeclaration( - String? name, { - Expression? initializer, - DartType type, - int flags, - bool isFinal, - bool isConst, - bool isInitializingFormal, - bool isSuperInitializingFormal, - bool isCovariantByDeclaration, - bool isLate, - bool isRequired, - bool isLowered, - bool isSynthesized, - bool isHoisted, - bool hasDeclaredInitializer, - bool isWildcard, - }) = VariableStatement; - - factory LegacyVariableDeclaration.forValue( - Expression? initializer, { - bool isFinal, - bool isConst, - bool isInitializingFormal, - bool isSuperInitializingFormal, - bool isLate, - bool isRequired, - bool isLowered, - DartType type, - }) = VariableStatement.forValue; -} - /// Declaration of a local variable. /// /// This may occur as a statement, but is also used in several non-statement @@ -1648,13 +1466,23 @@ abstract interface class LegacyVariableDeclaration /// When this occurs as a statement, it must be a direct child of a [Block]. // // DESIGN TODO: Should we remove the 'final' modifier from variables? -class VariableStatement extends Statement implements LegacyVariableDeclaration { +class VariableStatement extends Statement + implements Annotatable, VariableDeclaration { + /// Offset of the equals sign in the source file it comes from. + /// + /// Valid values are from 0 and up, or -1 ([TreeNode.noOffset]) + /// if the equals sign offset is not available (e.g. if not initialized) + /// (this is the default if none is specifically set). @override int fileEqualsOffset = TreeNode.noOffset; @override List? get fileOffsetsIfMultiple => [fileOffset, fileEqualsOffset]; + /// List of metadata annotations on the variable declaration. + /// + /// This defaults to an immutable empty list. Use [addAnnotation] to add + /// annotations if needed. @override List annotations = const []; @@ -1667,12 +1495,18 @@ class VariableStatement extends Statement implements LegacyVariableDeclaration { @override int flags = 0; + /// The declared or inferred type of the variable. @override DartType type; // Not null, defaults to dynamic. + /// Offset of the declaration, set and used when writing the binary. @override int binaryOffsetNoTag = -1; + /// For locals, this is the initial value. + /// For parameters, this is the default value. + /// + /// Should be null in other cases. @override Expression? initializer; // May be null. @@ -1741,6 +1575,10 @@ class VariableStatement extends Statement implements LegacyVariableDeclaration { this.isSynthesized = true; } + /// The name of the variable as provided in the source code. + /// + /// The name of a variable can only be omitted if the variable is synthesized. + /// Otherwise, its name is as provided in the source code. @override String? get name => _name; @@ -1780,48 +1618,102 @@ class VariableStatement extends Statement implements LegacyVariableDeclaration { static const int FlagSuperInitializingFormal = 1 << 12; static const int FlagErroneouslyInitialized = 1 << 13; + /// Whether the variable is declared with the `final` keyword. @override bool get isFinal => flags & FlagFinal != 0; + + /// Whether the variable is declared with the `const` keyword. @override bool get isConst => flags & FlagConst != 0; + /// Whether the parameter is declared with the `covariant` keyword. @override bool get isCovariantByDeclaration => flags & FlagCovariantByDeclaration != 0; + /// Whether the variable is declared as an initializing formal parameter of + /// a constructor. + @informative @override bool get isInitializingFormal => flags & FlagInitializingFormal != 0; + /// Whether the variable is declared as a super initializing formal parameter + /// of a constructor. + @informative @override bool get isSuperInitializingFormal => flags & FlagSuperInitializingFormal != 0; + @informative @override bool get isErroneouslyInitialized => flags & FlagErroneouslyInitialized != 0; + /// If this [LegacyVariableDeclaration] is a parameter of a method, indicates + /// whether the method implementation needs to contain a runtime type check to + /// deal with generic covariance. + /// + /// When `true`, runtime checks may need to be performed. @override bool get isCovariantByClass => flags & FlagCovariantByClass != 0; + /// Whether the variable is declared with the `late` keyword. + /// + /// The `late` modifier is only supported on local variables and not on + /// parameters. @override bool get isLate => flags & FlagLate != 0; + /// Whether the parameter is declared with the `required` keyword. + /// + /// The `required` modifier is only supported on named parameters and not on + /// positional parameters and local variables. @override bool get isRequired => flags & FlagRequired != 0; + /// Whether the variable is part of a lowering. + /// + /// If a variable is part of a lowering its name may be synthesized so that it + /// doesn't reflect the name used in the source code and might not have a + /// one-to-one correspondence with the variable in the source. + /// + /// Lowering is used for instance of encoding of 'this' in extension instance + /// members and encoding of late locals. @override bool get isLowered => flags & FlagLowered != 0; + /// Whether this variable is synthesized, that is, it is _not_ declared in + /// the source code. + /// + /// The name of a variable can only be omitted if the variable is synthesized. + /// Otherwise, its name is as provided in the source code. @override bool get isSynthesized => flags & FlagSynthesized != 0; + /// Whether the declaration of this variable is has been moved to an earlier + /// source location. + /// + /// This is for instance the case for variables declared in a pattern, where + /// the lowering requires the variable to be declared before the expression + /// that performs that matching in which its initialization occurs. @override bool get isHoisted => flags & FlagHoisted != 0; + /// Whether the variable has an initializer, either by declaration or copied + /// from an original declaration. + /// + /// Note that the variable might have a synthesized initializer expression, + /// so `hasDeclaredInitializer == false` doesn't imply `initializer == null`. + /// For instance, for duplicate variable names, an invalid expression is set + /// as the initializer of the second variable. @override bool get hasDeclaredInitializer => flags & FlagHasDeclaredInitializer != 0; @override 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 + /// is late final without an initializer. @override bool get isAssignable { if (isConst) return false; @@ -1996,12 +1888,12 @@ class VariableStatement extends Statement implements LegacyVariableDeclaration { } @override - VariableInitializationBase? get variableInitialization { + VariableDeclaration? get variableInitialization { throw new UnsupportedError("${this.runtimeType}"); } @override - void set variableInitialization(VariableInitializationBase? value) { + void set variableInitialization(VariableDeclaration? value) { throw new UnsupportedError("${this.runtimeType}"); } @@ -2127,47 +2019,6 @@ class FunctionDeclaration extends Statement implements LocalFunction { } } -/// The statement that marks the declaration of the variable in the source Dart -/// program. If the [initializer] is `null`, the variable was declared without -/// an initializer. -abstract class VariableInitializationBase - implements Statement, Annotatable, ContextConsumer { - abstract VariableDeclaration variable; - abstract Expression? initializer; - abstract bool hasDeclaredInitializer; - abstract int flags; - abstract bool isErroneouslyInitialized; - abstract bool isConst; - abstract bool isCovariantByClass; - abstract bool isCovariantByDeclaration; - abstract bool isFinal; - abstract bool isHoisted; - abstract bool isInitializingFormal; - abstract bool isLate; - abstract bool isLowered; - abstract bool isRequired; - abstract bool isSuperInitializingFormal; - abstract bool isSynthesized; - abstract bool isWildcard; - abstract int binaryOffsetNoTag; - abstract int fileEqualsOffset; - abstract String? name; - abstract DartType type; - abstract String? cosmeticName; - abstract VariableInitializationBase? variableInitialization; - - factory VariableInitializationBase({ - required VariableDeclaration variable, - required Expression? initializer, - bool hasDeclaredInitializer, - }) = VariableInitialization; - - void clearAnnotations(); - bool get isAssignable; - VariableContext get context; - VariableDeclaration get asExpressionVariable; -} - class VariableInitialization extends Statement implements VariableDeclaration { @override VariableDeclaration variable; @@ -2414,10 +2265,10 @@ class VariableInitialization extends Statement implements VariableDeclaration { } @override - VariableInitializationBase? get variableInitialization => this; + VariableDeclaration? get variableInitialization => this; @override - void set variableInitialization(VariableInitializationBase? value) { + void set variableInitialization(VariableDeclaration? value) { throw new UnsupportedError("${this.runtimeType}"); } diff --git a/pkg/kernel/lib/src/ast/variables.dart b/pkg/kernel/lib/src/ast/variables.dart index d39a24aa6c2..d843c809707 100644 --- a/pkg/kernel/lib/src/ast/variables.dart +++ b/pkg/kernel/lib/src/ast/variables.dart @@ -28,7 +28,7 @@ sealed class VariableBase extends TreeNode implements Annotatable { abstract interface class IVariable implements TreeNode { abstract DartType type; abstract String? cosmeticName; - abstract VariableInitializationBase? variableInitialization; + abstract VariableDeclaration? variableInitialization; abstract Expression? initializer; abstract bool isFinal; abstract bool isConst; @@ -73,14 +73,14 @@ abstract interface class IVariable implements TreeNode { /// The root of the sealed hierarchy of non-type variables. sealed class VariableDeclaration extends VariableBase - implements IVariable, Statement, VariableInitializationBase { + implements IVariable, Statement, ContextConsumer { /// Static type of the variable. @override abstract DartType type; /// Initialization node for the variable, if available. @override - abstract VariableInitializationBase? variableInitialization; + abstract VariableDeclaration? variableInitialization; /// Derived from [variableInitialization], if available. @override @@ -186,8 +186,7 @@ sealed class VariableDeclaration extends VariableBase @override VariableDeclaration get asExpressionVariable => this; - @override - String? get name; + abstract String? name; } /// Local variables. They aren't Statements. A [LocalVariable] is "declared" in @@ -202,7 +201,7 @@ class LocalVariable extends VariableDeclaration { DartType type; @override - VariableInitializationBase? variableInitialization; + VariableDeclaration? variableInitialization; @override List annotations = const []; @@ -532,12 +531,12 @@ class CatchVariable extends VariableDeclaration { } @override - VariableInitializationBase? get variableInitialization { + VariableDeclaration? get variableInitialization { throw new UnsupportedError("${this.runtimeType}.variableInitialization"); } @override - void set variableInitialization(VariableInitializationBase? value) { + void set variableInitialization(VariableDeclaration? value) { throw new UnsupportedError("${this.runtimeType}.variableInitialization="); } @@ -836,10 +835,10 @@ sealed class FunctionParameter extends VariableDeclaration { /// Function parameters don't have initializers, only default values. @override - VariableInitializationBase? get variableInitialization => null; + VariableDeclaration? get variableInitialization => null; @override - void set variableInitialization(VariableInitializationBase? value) {} + void set variableInitialization(VariableDeclaration? value) {} @override Expression? get initializer => defaultValue; @@ -1292,10 +1291,10 @@ class ThisVariable extends VariableDeclaration { void set cosmeticName(String? value) {} @override - VariableInitializationBase? get variableInitialization => null; + VariableDeclaration? get variableInitialization => null; @override - void set variableInitialization(VariableInitializationBase? value) {} + void set variableInitialization(VariableDeclaration? value) {} @override DartType type; @@ -1573,7 +1572,7 @@ class SyntheticVariable extends VariableDeclaration { DartType type; @override - VariableInitializationBase? variableInitialization; + VariableDeclaration? variableInitialization; // TODO(cstefantsova): Consider a throwing implementation instead. @override diff --git a/pkg/kernel/lib/src/equivalence.dart b/pkg/kernel/lib/src/equivalence.dart index 7521736f9f0..a4366593c44 100644 --- a/pkg/kernel/lib/src/equivalence.dart +++ b/pkg/kernel/lib/src/equivalence.dart @@ -1316,6 +1316,9 @@ class EquivalenceVisitor implements Visitor1 { if (a is LabeledStatement) { return b is LabeledStatement; } + if (a is VariableStatement) { + return b is VariableStatement && a.name == b.name; + } if (a is StructuralParameter) { return b is StructuralParameter && a.name == b.name; } @@ -5890,6 +5893,9 @@ class EquivalenceStrategy { if (identical(node, other)) return true; if (node is! VariableStatement) return false; if (other is! VariableStatement) return false; + if (!visitor.checkDeclarations(node, other, '')) { + return false; + } visitor.pushNodeState(node, other); bool result = true; if (!checkVariableStatement_fileEqualsOffset(visitor, node, other)) { diff --git a/pkg/kernel/lib/src/printer.dart b/pkg/kernel/lib/src/printer.dart index b3f59dceb86..cf7298deea4 100644 --- a/pkg/kernel/lib/src/printer.dart +++ b/pkg/kernel/lib/src/printer.dart @@ -260,7 +260,7 @@ class AstPrinter { return _variableNames[node] ??= '#${_variableNames.length}'; case CatchVariable(catchVariableName: var name): return name; - case LegacyVariableDeclaration(:var name): + case VariableStatement(:var name): case VariableInitialization(:var name): if (name != null) { return name; @@ -519,7 +519,7 @@ class AstPrinter { /// If [isLate] and [type] are provided, these values are used instead of /// the corresponding properties on [node]. void writeVariableInitialization( - VariableInitializationBase node, { + VariableDeclaration node, { bool includeModifiersAndType = true, bool? isLate, DartType? type, diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart index 44a73736366..bd643748917 100644 --- a/pkg/kernel/lib/text/ast_to_text.dart +++ b/pkg/kernel/lib/text/ast_to_text.dart @@ -243,7 +243,7 @@ class NameSystem { } abstract class Annotator { - String annotateVariable(Printer printer, VariableInitializationBase node); + String annotateVariable(Printer printer, VariableDeclaration node); String annotateReturn(Printer printer, FunctionNode node); String annotateField(Printer printer, Field node); } @@ -1249,7 +1249,8 @@ class Printer extends VisitorDefault with VisitorVoidMixin { } void writeExpressionVariable(VariableDeclaration node) { - if (node is LegacyVariableDeclaration && node is! FunctionParameter) { + // TODO(cstefantsova): Printer of the new variables is broken. + if (node is VariableStatement && node is! FunctionParameter) { writeVariableDeclaration(node); } else { if (showOffsets) writeWord("[${node.fileOffset}]"); @@ -1266,7 +1267,7 @@ class Printer extends VisitorDefault with VisitorVoidMixin { writeWord('this-variable'); case SyntheticVariable(): writeWord('synthetic-variable'); - case LegacyVariableDeclaration(): + case VariableStatement(): writeWord('variable-declaration'); case CatchVariable(): writeWord('catch-variable'); @@ -2603,7 +2604,7 @@ class Printer extends VisitorDefault with VisitorVoidMixin { ensureSpace(); } writeSymbol('('); - if (node.variable case LegacyVariableDeclaration variable) { + if (node.variable case VariableStatement variable) { writeVariableDeclaration(variable, useVarKeyword: true); } else { writeExpressionVariable(node.variable); @@ -2757,7 +2758,7 @@ class Printer extends VisitorDefault with VisitorVoidMixin { } @override - void visitVariableInitialization(VariableInitializationBase node) { + void visitVariableInitialization(VariableInitialization node) { writeIndentation(); writeVariableInitialization(node); _writeContexts(node); @@ -2825,8 +2826,8 @@ class Printer extends VisitorDefault with VisitorVoidMixin { } } - void writeVariableInitialization(VariableInitializationBase node) { - if (node is VariableDeclaration) { + void writeVariableInitialization(VariableDeclaration node) { + if (node is VariableStatement) { writeVariableDeclaration(node); } else { if (showOffsets) writeWord("[${node.fileOffset}]"); diff --git a/pkg/kernel/lib/type_checker.dart b/pkg/kernel/lib/type_checker.dart index c4c0560f002..0bfd15822d9 100644 --- a/pkg/kernel/lib/type_checker.dart +++ b/pkg/kernel/lib/type_checker.dart @@ -1183,7 +1183,7 @@ class TypeCheckingVisitor @override void visitForStatement(ForStatement node) { - node.variables.forEach(visitVariableInitialization); + node.variables.forEach(_handleVariableInitialization); if (node.condition != null) { node.condition = checkExpressionAndAssignability( node.condition!, @@ -1257,11 +1257,15 @@ class TypeCheckingVisitor @override void visitVariableDeclaration(VariableDeclaration node) { - visitVariableInitialization(node); + _handleVariableInitialization(node); } @override - void visitVariableInitialization(VariableInitializationBase node) { + void visitVariableInitialization(VariableInitialization node) { + _handleVariableInitialization(node); + } + + void _handleVariableInitialization(VariableDeclaration node) { if (node.initializer != null) { node.initializer = checkExpressionAndAssignability( node.initializer!, diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart index 3294bd9e832..428fff2e81d 100644 --- a/pkg/kernel/lib/verifier.dart +++ b/pkg/kernel/lib/verifier.dart @@ -270,7 +270,7 @@ class VerifyingVisitor extends RecursiveResultVisitor { // TODO(cstefantsova): Remove this method when the new variable model is // supported. bool _isNewModelVariable(TreeNode node) { - return node is VariableDeclaration && node is! LegacyVariableDeclaration || + return node is VariableDeclaration && node is! VariableStatement || node is FunctionParameter; } @@ -1142,11 +1142,11 @@ class VerifyingVisitor extends RecursiveResultVisitor { } @override - void visitVariableInitialization(VariableInitializationBase node) { + void visitVariableInitialization(VariableDeclaration node) { return _verifyVariableInitialization(node); } - void _verifyVariableInitialization(VariableInitializationBase node) { + void _verifyVariableInitialization(VariableDeclaration node) { enterTreeNode(node); TreeNode? parent = node.parent; if (parent is! Block && diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart index 8038364fd40..566680521b3 100644 --- a/pkg/kernel/lib/visitor.dart +++ b/pkg/kernel/lib/visitor.dart @@ -2425,18 +2425,6 @@ class RemovingTransformer extends TreeVisitor1Default { transformList(nodes, parent, dummyVariableDeclaration); } - /// Transforms or removes [VariableInitializationBase] nodes in [nodes] as - /// children of [parent]. - /// - /// This is convenience method for calling [transformList] with removal - /// sentinel for [VariableInitializationBase] nodes. - void transformVariableInitializationList( - List nodes, - TreeNode parent, - ) { - transformList(nodes, parent, dummyVariableDeclaration); - } - /// Transforms or removes [T] nodes in [nodes] as children of [parent] by /// calling [transformOrRemove] using [removalSentinel] as the removal /// sentinel. diff --git a/pkg/vm/lib/modular/transformations/for_in_lowering.dart b/pkg/vm/lib/modular/transformations/for_in_lowering.dart index 33aec7e9701..18f32dde866 100644 --- a/pkg/vm/lib/modular/transformations/for_in_lowering.dart +++ b/pkg/vm/lib/modular/transformations/for_in_lowering.dart @@ -279,7 +279,7 @@ class ForInLowering { return Block([syncForIteratorVariableInitialization, forStatement]); } - (VariableDeclaration, VariableInitializationBase) + (VariableDeclaration, VariableDeclaration) _createSyncForIteratorVariableAndInitialization({ required Expression initializer, required DartType type, @@ -290,7 +290,7 @@ class ForInLowering { cosmeticName: ForInVariables.syncForIterator, type: type, ); - final initialization = VariableInitializationBase( + final initialization = VariableInitialization( variable: variable, initializer: initializer, ); @@ -306,12 +306,12 @@ class ForInLowering { } } - VariableInitializationBase _ensureSyncForLoopVariableInitialization({ + VariableDeclaration _ensureSyncForLoopVariableInitialization({ required VariableDeclaration variable, required Expression initializer, }) { if (isClosureContextLoweringEnabled) { - return VariableInitializationBase( + return VariableInitialization( variable: variable, initializer: initializer, );