From 95df7312579d6b1ca2595971eca2510312edb565 Mon Sep 17 00:00:00 2001 From: Chloe Stefantsova Date: Mon, 8 Dec 2025 04:56:51 -0800 Subject: [PATCH] [cfe] Use VariableInitialization in ForStatement and similar nodes This change is similar to https://github.com/dart-lang/sdk/commit/c97e2554ee3b21b0b0146ca41be969dd8531986d, where ExpressionVariable was used in VariableGet and VariableSet. Part of https://github.com/dart-lang/sdk/issues/61572 Change-Id: Id9fa5302adcf8c773cec8478a31948b54c8393b6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/466720 Reviewed-by: Johnni Winther Commit-Queue: Chloe Stefantsova --- .../lib/src/kernel/body_builder.dart | 24 ++--- pkg/front_end/lib/src/kernel/collections.dart | 99 ++++++++++++------- .../lib/src/kernel/constant_evaluator.dart | 2 +- pkg/front_end/lib/src/kernel/forest.dart | 14 +-- .../lib/src/kernel/internal_ast.dart | 7 +- .../src/type_inference/inference_visitor.dart | 64 ++++++------ .../static_types/type_arguments_test.dart | 15 +++ .../local_variables.dart | 16 +++ .../local_variables.dart.strong.expect | 30 ++++++ ...local_variables.dart.strong.modular.expect | 30 ++++++ ...local_variables.dart.strong.outline.expect | 4 + ...ocal_variables.dart.textual_outline.expect | 4 + ...ables.dart.textual_outline_modelled.expect | 4 + pkg/front_end/tool/ast_model.dart | 2 +- pkg/kernel/lib/clone.dart | 3 +- pkg/kernel/lib/src/ast/expressions.dart | 2 +- pkg/kernel/lib/src/ast/statements.dart | 30 +++--- pkg/kernel/lib/src/equivalence.dart | 9 +- pkg/kernel/lib/src/printer.dart | 10 +- pkg/kernel/lib/text/ast_to_text.dart | 55 ++++++----- pkg/kernel/lib/type_checker.dart | 14 ++- pkg/kernel/lib/visitor.dart | 10 ++ 22 files changed, 307 insertions(+), 141 deletions(-) diff --git a/pkg/front_end/lib/src/kernel/body_builder.dart b/pkg/front_end/lib/src/kernel/body_builder.dart index 407a57db820..4b85e1b12dc 100644 --- a/pkg/front_end/lib/src/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/kernel/body_builder.dart @@ -3751,7 +3751,7 @@ class BodyBuilderImpl extends StackListenerImpl } } - List? _buildForLoopVariableDeclarations( + List? _buildForLoopVariableDeclarations( variableOrExpression, ) { // TODO(ahe): This can be simplified now that we have the events @@ -3759,40 +3759,40 @@ class BodyBuilderImpl extends StackListenerImpl if (variableOrExpression is Generator) { variableOrExpression = variableOrExpression.buildForEffect(); } - if (variableOrExpression is VariableDeclaration) { + if (variableOrExpression is VariableInitialization) { // Late for loop variables are not supported. An error has already been // reported by the parser. variableOrExpression.isLate = false; - return [variableOrExpression]; + return [variableOrExpression]; } else if (variableOrExpression is Expression) { VariableDeclaration variable = new VariableDeclarationImpl.forEffect( variableOrExpression, ); - return [variable]; + return [variable]; } else if (variableOrExpression is ExpressionStatement) { // Coverage-ignore-block(suite): Not run. VariableDeclaration variable = new VariableDeclarationImpl.forEffect( variableOrExpression.expression, ); - return [variable]; + return [variable]; } else if (forest.isVariablesDeclaration(variableOrExpression)) { return forest.variablesDeclarationExtractDeclarations( variableOrExpression, ); } else if (variableOrExpression is List) { // Coverage-ignore-block(suite): Not run. - List variables = []; + List variables = []; for (Object v in variableOrExpression) { variables.addAll(_buildForLoopVariableDeclarations(v)!); } return variables; } else if (variableOrExpression is PatternVariableDeclaration) { // Coverage-ignore-block(suite): Not run. - return []; + return []; } else if (variableOrExpression is ParserRecovery) { - return []; + return []; } else if (variableOrExpression == null) { - return []; + return []; } return null; } @@ -3991,7 +3991,7 @@ class BodyBuilderImpl extends StackListenerImpl .popNode(); Object? variableOrExpression = pop(); - List? variables; + List? variables; List? intermediateVariables; if (variableOrExpression is PatternVariableDeclaration) { variables = pop() as List; // Internal variables. @@ -4015,7 +4015,7 @@ class BodyBuilderImpl extends StackListenerImpl offsetForToken(forToken), patternVariableDeclaration: variableOrExpression, intermediateVariables: intermediateVariables!, - variables: variables, + variableInitializations: variables, condition: condition, updates: updates, body: entry, @@ -4110,7 +4110,7 @@ class BodyBuilderImpl extends StackListenerImpl .deferNode(); Object? variableOrExpression = pop(); - List? variables; + List? variables; List? intermediateVariables; if (variableOrExpression is PatternVariableDeclaration) { variables = pop() as List; diff --git a/pkg/front_end/lib/src/kernel/collections.dart b/pkg/front_end/lib/src/kernel/collections.dart index b0e2342861f..16de589bef2 100644 --- a/pkg/front_end/lib/src/kernel/collections.dart +++ b/pkg/front_end/lib/src/kernel/collections.dart @@ -270,8 +270,13 @@ class IfElement extends ControlFlowElement with ControlFlowElementMixin { class ForElement extends ControlFlowElement with ControlFlowElementMixin implements ForElementBase { + // May be empty, but not null. @override - final List variables; // May be empty, but not null. + final List variableInitializations; + + @override + // Coverage-ignore(suite): Not run. + List get variables => variableInitializations.cast(); @override Expression? condition; // May be null. @@ -282,8 +287,13 @@ class ForElement extends ControlFlowElement @override Expression body; - ForElement(this.variables, this.condition, this.updates, this.body) { - setParents(variables, this); + ForElement( + this.variableInitializations, + this.condition, + this.updates, + this.body, + ) { + setParents(variableInitializations, this); condition?.parent = this; setParents(updates, this); body.parent = this; @@ -292,7 +302,7 @@ class ForElement extends ControlFlowElement @override // Coverage-ignore(suite): Not run. void visitChildren(Visitor v) { - visitList(variables, v); + visitList(variableInitializations, v); condition?.accept(v); visitList(updates, v); body.accept(v); @@ -301,7 +311,7 @@ class ForElement extends ControlFlowElement @override // Coverage-ignore(suite): Not run. void transformChildren(Transformer v) { - v.transformList(variables, this); + v.transformList(variableInitializations, this); if (condition != null) { condition = v.transform(condition!); condition?.parent = this; @@ -314,7 +324,7 @@ class ForElement extends ControlFlowElement @override // Coverage-ignore(suite): Not run. void transformOrRemoveChildren(RemovingTransformer v) { - v.transformVariableDeclarationList(variables, this); + v.transformVariableInitializationList(variableInitializations, this); if (condition != null) { condition = v.transformOrRemoveExpression(condition!); condition?.parent = this; @@ -336,7 +346,7 @@ class ForElement extends ControlFlowElement } if (bodyEntry == null) return null; ForMapEntry result = new ForMapEntry( - variables, + variableInitializations, condition, updates, bodyEntry, @@ -354,12 +364,12 @@ class ForElement extends ControlFlowElement // Coverage-ignore(suite): Not run. void toTextInternal(AstPrinter printer) { printer.write('for ('); - for (int index = 0; index < variables.length; index++) { + for (int index = 0; index < variableInitializations.length; index++) { if (index > 0) { printer.write(', '); } - printer.writeVariableDeclaration( - variables[index], + printer.writeVariableInitialization( + variableInitializations[index], includeModifiersAndType: index == 0, ); } @@ -593,6 +603,8 @@ class IfCaseElement extends ControlFlowElementImpl } abstract interface class ForElementBase implements AuxiliaryExpression { + List get variableInitializations; + List get variables; abstract Expression? condition; @@ -608,8 +620,13 @@ class PatternForElement extends ControlFlowElementImpl PatternVariableDeclaration patternVariableDeclaration; List intermediateVariables; + // May be empty, but not null. @override - final List variables; // May be empty, but not null. + final List variableInitializations; + + @override + // Coverage-ignore(suite): Not run. + List get variables => variableInitializations.cast(); @override Expression? condition; // May be null. @@ -623,11 +640,11 @@ class PatternForElement extends ControlFlowElementImpl PatternForElement({ required this.patternVariableDeclaration, required this.intermediateVariables, - required this.variables, + required List variables, required this.condition, required this.updates, required this.body, - }); + }) : variableInitializations = variables; @override ExpressionInferenceResult acceptInference( @@ -642,12 +659,12 @@ class PatternForElement extends ControlFlowElementImpl void toTextInternal(AstPrinter printer) { patternVariableDeclaration.toTextInternal(printer); printer.write('for ('); - for (int index = 0; index < variables.length; index++) { + for (int index = 0; index < variableInitializations.length; index++) { if (index > 0) { printer.write(', '); } - printer.writeVariableDeclaration( - variables[index], + printer.writeVariableInitialization( + variableInitializations[index], includeModifiersAndType: index == 0, ); } @@ -893,6 +910,8 @@ class IfMapEntry extends TreeNode } abstract interface class ForMapEntryBase implements TreeNode, MapLiteralEntry { + List get variableInitializations; + List get variables; abstract Expression? condition; @@ -906,8 +925,13 @@ abstract interface class ForMapEntryBase implements TreeNode, MapLiteralEntry { class ForMapEntry extends TreeNode with ControlFlowMapEntryMixin implements ForMapEntryBase, ControlFlowMapEntry { + // May be empty, but not null. @override - final List variables; // May be empty, but not null. + final List variableInitializations; + + @override + // Coverage-ignore(suite): Not run. + List get variables => variableInitializations.cast(); @override Expression? condition; // May be null. @@ -918,8 +942,13 @@ class ForMapEntry extends TreeNode @override MapLiteralEntry body; - ForMapEntry(this.variables, this.condition, this.updates, this.body) { - setParents(variables, this); + ForMapEntry( + this.variableInitializations, + this.condition, + this.updates, + this.body, + ) { + setParents(variableInitializations, this); condition?.parent = this; setParents(updates, this); body.parent = this; @@ -928,7 +957,7 @@ class ForMapEntry extends TreeNode @override // Coverage-ignore(suite): Not run. void visitChildren(Visitor v) { - visitList(variables, v); + visitList(variableInitializations, v); condition?.accept(v); visitList(updates, v); body.accept(v); @@ -937,7 +966,7 @@ class ForMapEntry extends TreeNode @override // Coverage-ignore(suite): Not run. void transformChildren(Transformer v) { - v.transformList(variables, this); + v.transformList(variableInitializations, this); if (condition != null) { condition = v.transform(condition!); condition?.parent = this; @@ -950,7 +979,7 @@ class ForMapEntry extends TreeNode @override // Coverage-ignore(suite): Not run. void transformOrRemoveChildren(RemovingTransformer v) { - v.transformVariableDeclarationList(variables, this); + v.transformVariableInitializationList(variableInitializations, this); if (condition != null) { condition = v.transformOrRemoveExpression(condition!); condition?.parent = this; @@ -969,12 +998,12 @@ class ForMapEntry extends TreeNode // Coverage-ignore(suite): Not run. void toTextInternal(AstPrinter printer) { printer.write('for ('); - for (int index = 0; index < variables.length; index++) { + for (int index = 0; index < variableInitializations.length; index++) { if (index > 0) { printer.write(', '); } - printer.writeVariableDeclaration( - variables[index], + printer.writeVariableInitialization( + variableInitializations[index], includeModifiersAndType: index == 0, ); } @@ -996,7 +1025,11 @@ class PatternForMapEntry extends TreeNode List intermediateVariables; @override - final List variables; + final List variableInitializations; + + @override + // Coverage-ignore(suite): Not run. + List get variables => variableInitializations.cast(); @override Expression? condition; @@ -1010,23 +1043,23 @@ class PatternForMapEntry extends TreeNode PatternForMapEntry({ required this.patternVariableDeclaration, required this.intermediateVariables, - required this.variables, + required List variables, required this.condition, required this.updates, required this.body, - }); + }) : variableInitializations = variables; @override // Coverage-ignore(suite): Not run. void toTextInternal(AstPrinter printer) { patternVariableDeclaration.toTextInternal(printer); printer.write('for ('); - for (int index = 0; index < variables.length; index++) { + for (int index = 0; index < variableInitializations.length; index++) { if (index > 0) { printer.write(', '); } - printer.writeVariableDeclaration( - variables[index], + printer.writeVariableInitialization( + variableInitializations[index], includeModifiersAndType: index == 0, ); } @@ -1313,7 +1346,7 @@ MapLiteralEntry convertToMapEntry( PatternForMapEntry result = new PatternForMapEntry( patternVariableDeclaration: element.patternVariableDeclaration, intermediateVariables: element.intermediateVariables, - variables: element.variables, + variables: element.variableInitializations, condition: element.condition, updates: element.updates, body: convertToMapEntry( @@ -1329,7 +1362,7 @@ MapLiteralEntry convertToMapEntry( case ForElement(): ForMapEntry result = new ForMapEntry( - element.variables, + element.variableInitializations, element.condition, element.updates, convertToMapEntry( diff --git a/pkg/front_end/lib/src/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/kernel/constant_evaluator.dart index 21c553eb339..41a1dbdcff2 100644 --- a/pkg/front_end/lib/src/kernel/constant_evaluator.dart +++ b/pkg/front_end/lib/src/kernel/constant_evaluator.dart @@ -6052,7 +6052,7 @@ class StatementConstantEvaluator @override ExecutionStatus visitForStatement(ForStatement node) { - for (VariableDeclaration variable in node.variables) { + for (VariableInitialization variable in node.variableInitializations) { final ExecutionStatus status = variable.accept(this); if (status is! ProceedStatus) return status; } diff --git a/pkg/front_end/lib/src/kernel/forest.dart b/pkg/front_end/lib/src/kernel/forest.dart index e4c1711fa45..589709d38b8 100644 --- a/pkg/front_end/lib/src/kernel/forest.dart +++ b/pkg/front_end/lib/src/kernel/forest.dart @@ -282,7 +282,7 @@ class Forest { ForElement createForElement( int fileOffset, - List variables, + List variables, Expression? condition, List updates, Expression body, @@ -295,7 +295,7 @@ class Forest { int fileOffset, { required PatternVariableDeclaration patternVariableDeclaration, required List intermediateVariables, - required List variables, + required List variables, required Expression? condition, required List updates, required Expression body, @@ -312,7 +312,7 @@ class Forest { ForMapEntry createForMapEntry( int fileOffset, - List variables, + List variables, Expression? condition, List updates, MapLiteralEntry body, @@ -325,7 +325,7 @@ class Forest { int fileOffset, { required PatternVariableDeclaration patternVariableDeclaration, required List intermediateVariables, - required List variables, + required List variableInitializations, required Expression? condition, required List updates, required MapLiteralEntry body, @@ -333,7 +333,7 @@ class Forest { return new PatternForMapEntry( patternVariableDeclaration: patternVariableDeclaration, intermediateVariables: intermediateVariables, - variables: variables, + variables: variableInitializations, condition: condition, updates: updates, body: body, @@ -505,7 +505,7 @@ class Forest { /// Return a representation of a for statement. Statement createForStatement( int fileOffset, - List? variables, + List? variables, Expression? condition, List updaters, Statement body, @@ -1219,7 +1219,7 @@ class _VariablesDeclaration extends AuxiliaryStatement { if (index > 0) { printer.write(', '); } - printer.writeVariableDeclaration( + printer.writeVariableInitialization( declarations[index], includeModifiersAndType: index == 0, ); diff --git a/pkg/front_end/lib/src/kernel/internal_ast.dart b/pkg/front_end/lib/src/kernel/internal_ast.dart index fae713d353b..ecd1b302180 100644 --- a/pkg/front_end/lib/src/kernel/internal_ast.dart +++ b/pkg/front_end/lib/src/kernel/internal_ast.dart @@ -385,7 +385,7 @@ class Cascade extends InternalExpression { // Coverage-ignore(suite): Not run. void toTextInternal(AstPrinter printer) { printer.write('let '); - printer.writeVariableDeclaration(variable); + printer.writeVariableInitialization(variable); printer.write(' in cascade {'); printer.incIndentation(); for (Expression expression in expressions) { @@ -431,7 +431,7 @@ class DeferredCheck extends InternalExpression { // Coverage-ignore(suite): Not run. void toTextInternal(AstPrinter printer) { printer.write('let '); - printer.writeVariableDeclaration(variable); + printer.writeVariableInitialization(variable); printer.write(' in '); printer.writeExpression(expression); } @@ -888,7 +888,7 @@ class VariableDeclarationImpl extends VariableStatement @override // Coverage-ignore(suite): Not run. void toTextInternal(AstPrinter printer) { - printer.writeVariableDeclaration( + printer.writeVariableInitialization( this, isLate: isLate || lateGetter != null, type: lateType ?? type, @@ -1050,7 +1050,6 @@ mixin DelegatingVariableMixin on InternalExpressionVariableMixin bool get isLate => astVariable.isLate; @override - // Coverage-ignore(suite): Not run. void set isLate(bool value) { astVariable.isLate = value; } 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 ee2ad409a12..1b256b6ec47 100644 --- a/pkg/front_end/lib/src/type_inference/inference_visitor.dart +++ b/pkg/front_end/lib/src/type_inference/inference_visitor.dart @@ -3477,9 +3477,9 @@ class InferenceVisitorImpl extends InferenceVisitorBase @override StatementInferenceResult visitForStatement(ForStatement node) { _contextAllocationStrategy.enterScopeProvider(node); - List? variables; - for (int index = 0; index < node.variables.length; index++) { - VariableDeclaration variable = node.variables[index]; + List? variables; + for (int index = 0; index < node.variableInitializations.length; index++) { + VariableInitialization variable = node.variableInitializations[index]; if (variable.name == null) { if (variable.initializer != null) { ExpressionInferenceResult result = inferExpression( @@ -3495,8 +3495,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase if (variableResult.hasChanged) { // Coverage-ignore-block(suite): Not run. if (variables == null) { - variables = []; - variables.addAll(node.variables.sublist(0, index)); + variables = []; + variables.addAll(node.variableInitializations.sublist(0, index)); } if (variableResult.statementCount == 1) { variables.add(variableResult.statement as VariableDeclaration); @@ -3514,8 +3514,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase } if (variables != null) { // Coverage-ignore-block(suite): Not run. - node.variables.clear(); - node.variables.addAll(variables); + node.variableInitializations.clear(); + node.variableInitializations.addAll(variables); setParents(variables, node); } flowAnalysis.for_conditionBegin(node); @@ -4278,11 +4278,11 @@ class InferenceVisitorImpl extends InferenceVisitorBase List declaredVariables = patternVariableDeclaration.pattern.declaredVariables; assert(declaredVariables.length == element.intermediateVariables.length); - assert(declaredVariables.length == element.variables.length); + assert(declaredVariables.length == element.variableInitializations.length); for (int i = 0; i < declaredVariables.length; i++) { DartType type = declaredVariables[i].type; element.intermediateVariables[i].type = type; - element.variables[i].type = type; + element.variableInitializations[i].type = type; } return _inferForElementBase( @@ -4314,9 +4314,13 @@ class InferenceVisitorImpl extends InferenceVisitorBase Map inferredConditionTypes, ) { // TODO(johnniwinther): Use _visitStatements instead. - List? variables; - for (int index = 0; index < element.variables.length; index++) { - VariableDeclaration variable = element.variables[index]; + List? variables; + for ( + int index = 0; + index < element.variableInitializations.length; + index++ + ) { + VariableInitialization variable = element.variableInitializations[index]; if (variable.name == null) { if (variable.initializer != null) { ExpressionInferenceResult initializerResult = inferExpression( @@ -4334,7 +4338,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase // Coverage-ignore-block(suite): Not run. if (variables == null) { variables = []; - variables.addAll(element.variables.sublist(0, index)); + variables.addAll(element.variableInitializations.sublist(0, index)); } if (variableResult.statementCount == 1) { variables.add(variableResult.statement as VariableDeclaration); @@ -4352,8 +4356,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase } if (variables != null) { // Coverage-ignore-block(suite): Not run. - element.variables.clear(); - element.variables.addAll(variables); + element.variableInitializations.clear(); + element.variableInitializations.addAll(variables); setParents(variables, element); } @@ -5172,7 +5176,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase : _createBlock(statements); ForStatement loop = _createForStatement( element.fileOffset, - element.variables, + element.variableInitializations, element.condition, element.updates, loopBody, @@ -5207,7 +5211,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase _createBlock(statements); ForStatement loop = _createForStatement( element.fileOffset, - element.variables, + element.variableInitializations, element.condition, element.updates, loopBody, @@ -5774,7 +5778,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase : _createBlock(statements); ForStatement loop = _createForStatement( entry.fileOffset, - entry.variables, + entry.variableInitializations, entry.condition, entry.updates, loopBody, @@ -5807,7 +5811,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase : _createBlock(statements); ForStatement loop = _createForStatement( entry.fileOffset, - entry.variables, + entry.variableInitializations, entry.condition, entry.updates, loopBody, @@ -6771,7 +6775,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase ForStatement _createForStatement( int fileOffset, - List variables, + List variables, Expression? condition, List updates, Statement body, @@ -7367,11 +7371,11 @@ class InferenceVisitorImpl extends InferenceVisitorBase List declaredVariables = patternVariableDeclaration.pattern.declaredVariables; assert(declaredVariables.length == entry.intermediateVariables.length); - assert(declaredVariables.length == entry.variables.length); + assert(declaredVariables.length == entry.variableInitializations.length); for (int i = 0; i < declaredVariables.length; i++) { DartType type = declaredVariables[i].type; entry.intermediateVariables[i].type = type; - entry.variables[i].type = type; + entry.variableInitializations[i].type = type; } return _inferForMapEntryBase( @@ -7427,9 +7431,9 @@ class InferenceVisitorImpl extends InferenceVisitorBase _MapLiteralEntryOffsets offsets, ) { // TODO(johnniwinther): Use _visitStatements instead. - List? variables; - for (int index = 0; index < entry.variables.length; index++) { - VariableDeclaration variable = entry.variables[index]; + List? variables; + for (int index = 0; index < entry.variableInitializations.length; index++) { + VariableInitialization variable = entry.variableInitializations[index]; if (variable.name == null) { if (variable.initializer != null) { ExpressionInferenceResult result = inferExpression( @@ -7446,7 +7450,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase // Coverage-ignore-block(suite): Not run. if (variables == null) { variables = []; - variables.addAll(entry.variables.sublist(0, index)); + variables.addAll(entry.variableInitializations.sublist(0, index)); } if (variableResult.statementCount == 1) { variables.add(variableResult.statement as VariableDeclaration); @@ -7464,8 +7468,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase } if (variables != null) { // Coverage-ignore-block(suite): Not run. - entry.variables.clear(); - entry.variables.addAll(variables); + entry.variableInitializations.clear(); + entry.variableInitializations.addAll(variables); setParents(variables, entry); } @@ -8211,7 +8215,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase PatternForElement result = new PatternForElement( patternVariableDeclaration: entry.patternVariableDeclaration, intermediateVariables: entry.intermediateVariables, - variables: entry.variables, + variables: entry.variableInitializations, condition: entry.condition, updates: entry.updates, body: convertToElement(entry.body, onConvertMapEntry), @@ -8220,7 +8224,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase return result; case ForMapEntry(): ForElement result = new ForElement( - entry.variables, + entry.variableInitializations, entry.condition, entry.updates, convertToElement(entry.body, onConvertMapEntry), 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 95647ee34d4..bab290e07f8 100644 --- a/pkg/front_end/test/static_types/type_arguments_test.dart +++ b/pkg/front_end/test/static_types/type_arguments_test.dart @@ -69,6 +69,8 @@ class TypeArgumentsVisitor extends VerifyingAnalysis { 'VariableDeclaration', uri: astUri, ); + InterfaceType variableInitializationType = interface + .createInterfaceType('VariableInitialization', uri: astUri); DartType typeArgument = receiver.arguments.types.single; if (interface.isSubtypeOf(typeArgument, expressionType) && typeArgument != expressionType) { @@ -102,6 +104,19 @@ class TypeArgumentsVisitor extends VerifyingAnalysis { "${typeArgument} instead of ${variableDeclarationType}", ); } + } else if (interface.isSubtypeOf( + typeArgument, + variableInitializationType, + )) { + // [VariableInitialization] is used as an exclusive member of, for + // instance, `ForStatement.variableInitializations`. + if (typeArgument != variableInitializationType) { + registerError( + node, + "map().toList() with type argument " + "${typeArgument} instead of ${variableInitializationType}", + ); + } } else if (typeArgument != statementType) { registerError( node, diff --git a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart index d0aabd33c14..781f3fcfd52 100644 --- a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart +++ b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart @@ -11,3 +11,19 @@ testCaptured() { int a = 0; return () => a; } + +testForCounterNotCaptured() { + int a = 0; + for (int i = 0; i < 10; i++) { + a += i; + } + return a; +} + +testForCounterCaptured() { + List closures = []; + for (int i = 0; i < 10; i++) { + closures.add(() => i); + } + return closures; +} diff --git a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.expect b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.expect index 40accb810ba..92dbb5c488f 100644 --- a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.expect +++ b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.expect @@ -18,3 +18,33 @@ static method testCaptured() → dynamic /* scope=[ a := 0; return () → core::int => a; } +static method testForCounterNotCaptured() → dynamic /* scope=[ + not-captured VariableContext([ + local-variable a; + ]), +] */ { + a := 0; + for /* scope=[ + not-captured VariableContext([ + local-variable i; + ]), + ] */ (i := 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) { + a = a.{core::num::+}(i){(core::num) → core::int}; + } + return a; +} +static method testForCounterCaptured() → dynamic /* scope=[ + not-captured VariableContext([ + local-variable closures; + ]), +] */ { + closures := []; + for /* scope=[ + captured VariableContext([ + local-variable i; + ]), + ] */ (i := 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) { + closures.{core::List::add}(() → core::int => i){(core::Function) → void}; + } + return closures; +} diff --git a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.modular.expect b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.modular.expect index 40accb810ba..92dbb5c488f 100644 --- a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.modular.expect +++ b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.modular.expect @@ -18,3 +18,33 @@ static method testCaptured() → dynamic /* scope=[ a := 0; return () → core::int => a; } +static method testForCounterNotCaptured() → dynamic /* scope=[ + not-captured VariableContext([ + local-variable a; + ]), +] */ { + a := 0; + for /* scope=[ + not-captured VariableContext([ + local-variable i; + ]), + ] */ (i := 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) { + a = a.{core::num::+}(i){(core::num) → core::int}; + } + return a; +} +static method testForCounterCaptured() → dynamic /* scope=[ + not-captured VariableContext([ + local-variable closures; + ]), +] */ { + closures := []; + for /* scope=[ + captured VariableContext([ + local-variable i; + ]), + ] */ (i := 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) { + closures.{core::List::add}(() → core::int => i){(core::Function) → void}; + } + return closures; +} diff --git a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.outline.expect b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.outline.expect index 56ece2a73d4..9312b6d9ef1 100644 --- a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.outline.expect +++ b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.strong.outline.expect @@ -5,3 +5,7 @@ static method testNotCaptured() → dynamic ; static method testCaptured() → dynamic ; +static method testForCounterNotCaptured() → dynamic + ; +static method testForCounterCaptured() → dynamic + ; diff --git a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.textual_outline.expect b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.textual_outline.expect index cff0a042805..f24f14b3e70 100644 --- a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.textual_outline.expect +++ b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.textual_outline.expect @@ -1,3 +1,7 @@ testNotCaptured() {} testCaptured() {} + +testForCounterNotCaptured() {} + +testForCounterCaptured() {} diff --git a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.textual_outline_modelled.expect index 197c7f3c6eb..452a2e01c3b 100644 --- a/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.textual_outline_modelled.expect +++ b/pkg/front_end/testcases/closure_context_lowering/local_variables.dart.textual_outline_modelled.expect @@ -1,3 +1,7 @@ testCaptured() {} +testForCounterCaptured() {} + +testForCounterNotCaptured() {} + testNotCaptured() {} diff --git a/pkg/front_end/tool/ast_model.dart b/pkg/front_end/tool/ast_model.dart index 4febd64aabd..9edefbb7898 100644 --- a/pkg/front_end/tool/ast_model.dart +++ b/pkg/front_end/tool/ast_model.dart @@ -129,7 +129,7 @@ const Map> _fieldRuleMap = { 'LocalFunctionInvocation': {'variable': FieldRule(isDeclaration: false)}, 'LocalVariable': {'variableInitialization': FieldRule(isDeclaration: false)}, 'BreakStatement': {'target': FieldRule(isDeclaration: false)}, - 'ForStatement': {'variables': FieldRule(isDeclaration: true)}, + 'ForStatement': {'variableInitializations': FieldRule(isDeclaration: true)}, 'ForInStatement': {'variable': FieldRule(isDeclaration: true)}, 'SwitchStatement': {'cases': FieldRule(isDeclaration: true)}, 'ContinueSwitchStatement': {'target': FieldRule(isDeclaration: false)}, diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart index 3b245af23d4..d716e1f3cda 100644 --- a/pkg/kernel/lib/clone.dart +++ b/pkg/kernel/lib/clone.dart @@ -534,7 +534,8 @@ class CloneVisitorNotMembers @override TreeNode visitForStatement(ForStatement node) { - List variables = node.variables.map(clone).toList(); + List variables = + node.variableInitializations.map(clone).toList(); return new ForStatement(variables, cloneOptional(node.condition), node.updates.map(clone).toList(), clone(node.body)); } diff --git a/pkg/kernel/lib/src/ast/expressions.dart b/pkg/kernel/lib/src/ast/expressions.dart index 851ec628ca2..2948ac43c5e 100644 --- a/pkg/kernel/lib/src/ast/expressions.dart +++ b/pkg/kernel/lib/src/ast/expressions.dart @@ -4845,7 +4845,7 @@ class Let extends Expression { @override void toTextInternal(AstPrinter printer) { printer.write('let '); - printer.writeVariableDeclaration(variable); + printer.writeVariableInitialization(variable); printer.write(' in '); printer.writeExpression(body); } diff --git a/pkg/kernel/lib/src/ast/statements.dart b/pkg/kernel/lib/src/ast/statements.dart index 772b69b8a25..28c9ef5a018 100644 --- a/pkg/kernel/lib/src/ast/statements.dart +++ b/pkg/kernel/lib/src/ast/statements.dart @@ -530,7 +530,10 @@ class DoStatement extends Statement implements LoopStatement { } class ForStatement extends Statement implements LoopStatement, ScopeProvider { - final List variables; // May be empty, but not null. + // May be empty, but not null. + final List variableInitializations; + List get variables => variableInitializations.cast(); + Expression? condition; // May be null. final List updates; // May be empty, but not null. @@ -540,8 +543,9 @@ class ForStatement extends Statement implements LoopStatement, ScopeProvider { @override Scope? scope; - ForStatement(this.variables, this.condition, this.updates, this.body) { - setParents(variables, this); + ForStatement( + this.variableInitializations, this.condition, this.updates, this.body) { + setParents(variableInitializations, this); condition?.parent = this; setParents(updates, this); body.parent = this; @@ -556,7 +560,7 @@ class ForStatement extends Statement implements LoopStatement, ScopeProvider { @override void visitChildren(Visitor v) { - visitList(variables, v); + visitList(variableInitializations, v); condition?.accept(v); visitList(updates, v); body.accept(v); @@ -564,7 +568,7 @@ class ForStatement extends Statement implements LoopStatement, ScopeProvider { @override void transformChildren(Transformer v) { - v.transformList(variables, this); + v.transformList(variableInitializations, this); if (condition != null) { condition = v.transform(condition!); condition?.parent = this; @@ -576,7 +580,7 @@ class ForStatement extends Statement implements LoopStatement, ScopeProvider { @override void transformOrRemoveChildren(RemovingTransformer v) { - v.transformVariableDeclarationList(variables, this); + v.transformVariableInitializationList(variableInitializations, this); if (condition != null) { condition = v.transformOrRemoveExpression(condition!); condition?.parent = this; @@ -594,11 +598,11 @@ class ForStatement extends Statement implements LoopStatement, ScopeProvider { @override void toTextInternal(AstPrinter printer) { printer.write('for ('); - for (int index = 0; index < variables.length; index++) { + for (int index = 0; index < variableInitializations.length; index++) { if (index > 0) { printer.write(', '); } - printer.writeVariableDeclaration(variables[index], + printer.writeVariableInitialization(variableInitializations[index], includeModifiersAndType: index == 0); } printer.write('; '); @@ -761,7 +765,7 @@ class ForInStatement extends Statement implements LoopStatement, ScopeProvider { @override void toTextInternal(AstPrinter printer) { printer.write('for ('); - printer.writeVariableDeclaration(variable); + printer.writeVariableInitialization(variable); printer.write(' in '); printer.writeExpression(iterable); @@ -1274,11 +1278,11 @@ class Catch extends TreeNode implements ScopeProvider { printer.write(' '); } printer.write('catch ('); - printer.writeVariableDeclaration(exception!, + printer.writeVariableInitialization(exception!, includeModifiersAndType: false); if (stackTrace != null) { printer.write(', '); - printer.writeVariableDeclaration(stackTrace!, + printer.writeVariableInitialization(stackTrace!, includeModifiersAndType: false); } printer.write(') '); @@ -1898,13 +1902,13 @@ class VariableStatement extends Statement implements VariableDeclaration { @override String toStringInternal() { AstPrinter printer = new AstPrinter(defaultAstTextStrategy); - printer.writeVariableDeclaration(this, includeInitializer: false); + printer.writeVariableInitialization(this, includeInitializer: false); return printer.getText(); } @override void toTextInternal(AstPrinter printer) { - printer.writeVariableDeclaration(this); + printer.writeVariableInitialization(this); printer.write(';'); } diff --git a/pkg/kernel/lib/src/equivalence.dart b/pkg/kernel/lib/src/equivalence.dart index b43c313231c..dccdab82383 100644 --- a/pkg/kernel/lib/src/equivalence.dart +++ b/pkg/kernel/lib/src/equivalence.dart @@ -5131,7 +5131,7 @@ class EquivalenceStrategy { if (other is! ForStatement) return false; visitor.pushNodeState(node, other); bool result = true; - if (!checkForStatement_variables(visitor, node, other)) { + if (!checkForStatement_variableInitializations(visitor, node, other)) { result = visitor.resultOnInequivalence; } if (!checkForStatement_condition(visitor, node, other)) { @@ -9542,10 +9542,13 @@ class EquivalenceStrategy { return checkStatement_fileOffset(visitor, node, other); } - bool checkForStatement_variables( + bool checkForStatement_variableInitializations( EquivalenceVisitor visitor, ForStatement node, ForStatement other) { return visitor.checkLists( - node.variables, other.variables, visitor.checkNodes, 'variables'); + node.variableInitializations, + other.variableInitializations, + visitor.checkNodes, + 'variableInitializations'); } bool checkForStatement_condition( diff --git a/pkg/kernel/lib/src/printer.dart b/pkg/kernel/lib/src/printer.dart index 21bee4e37e8..b884609a511 100644 --- a/pkg/kernel/lib/src/printer.dart +++ b/pkg/kernel/lib/src/printer.dart @@ -467,7 +467,7 @@ class AstPrinter { node.toTextInternal(this, includeTypeArguments: includeTypeArguments); } - /// Writes the variable declaration [node] to the printer buffer. + /// Writes the [VariableInitialization] [node] to the printer buffer. /// /// If [includeModifiersAndType] is `true`, the declaration is prefixed by /// the modifiers and declared type of the variable. Otherwise only the @@ -475,7 +475,7 @@ class AstPrinter { /// /// If [isLate] and [type] are provided, these values are used instead of /// the corresponding properties on [node]. - void writeVariableDeclaration(VariableDeclaration node, + void writeVariableInitialization(VariableInitialization node, {bool includeModifiersAndType = true, bool? isLate, DartType? type, @@ -496,7 +496,7 @@ class AstPrinter { writeType(type ?? node.type); _sb.write(' '); } - _sb.write(getVariableName(node)); + _sb.write(getVariableName(node.variable)); if (includeInitializer && node.initializer != null && !node.isRequired) { _sb.write(' = '); writeExpression(node.initializer!); @@ -556,7 +556,7 @@ class AstPrinter { if (index == node.requiredParameterCount) { _sb.write('['); } - writeVariableDeclaration(node.positionalParameters[index]); + writeVariableInitialization(node.positionalParameters[index]); } if (node.requiredParameterCount < node.positionalParameters.length) { _sb.write(']'); @@ -570,7 +570,7 @@ class AstPrinter { if (index > 0) { _sb.write(', '); } - writeVariableDeclaration(node.namedParameters[index]); + writeVariableInitialization(node.namedParameters[index]); } _sb.write('}'); } diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart index bdcab9d3d00..27e336e359e 100644 --- a/pkg/kernel/lib/text/ast_to_text.dart +++ b/pkg/kernel/lib/text/ast_to_text.dart @@ -224,7 +224,7 @@ class NameSystem { } abstract class Annotator { - String annotateVariable(Printer printer, VariableDeclaration node); + String annotateVariable(Printer printer, VariableInitialization node); String annotateReturn(Printer printer, FunctionNode node); String annotateField(Printer printer, Field node); } @@ -1103,6 +1103,7 @@ class Printer extends VisitorDefault with VisitorVoidMixin { writeVariableContext(context, separator: separator); } --indentation; + writeIndentation(); writeWord('] */'); } @@ -2422,8 +2423,12 @@ class Printer extends VisitorDefault with VisitorVoidMixin { void visitForStatement(ForStatement node) { writeIndentation(); writeSpaced('for'); + if (node.scope case Scope scope?) { + writeScope(scope); + ensureSpace(); + } writeSymbol('('); - writeList(node.variables, writeVariableDeclaration); + writeList(node.variableInitializations, writeVariableInitialization); writeComma(';'); Expression? condition = node.condition; if (condition != null) { @@ -2647,26 +2652,32 @@ class Printer extends VisitorDefault with VisitorVoidMixin { void writeVariableInitialization( VariableInitialization node, ) { - if (showOffsets) writeWord("[${node.fileOffset}]"); - if (showMetadata) writeMetadata(node); - writeAnnotationList(node.annotations, separateLines: false); - writeModifier(node.isErroneouslyInitialized, 'erroneously-initialized'); - bool hasImplicitInitializer = node.initializer is NullLiteral || - (node.initializer is ConstantExpression && - (node.initializer as ConstantExpression).constant is NullConstant); - if ((node.initializer == null || hasImplicitInitializer) && - node.hasDeclaredInitializer) { - writeModifier(node.hasDeclaredInitializer, 'has-declared-initializer'); - } else if (node.initializer != null && - !hasImplicitInitializer && - !node.hasDeclaredInitializer) { - writeModifier(node.hasDeclaredInitializer, 'has-no-declared-initializer'); - } - writeWord(getVariableName(node.variable)); - Expression? initializer = node.initializer; - if (initializer != null) { - writeSpaced(':='); - writeExpression(initializer); + if (node is VariableDeclaration) { + writeVariableDeclaration(node); + } else { + if (showOffsets) writeWord("[${node.fileOffset}]"); + if (showMetadata) writeMetadata(node); + writeAnnotationList(node.annotations, separateLines: false); + writeModifier(node.isErroneouslyInitialized, 'erroneously-initialized'); + bool hasImplicitInitializer = node.initializer is NullLiteral || + (node.initializer is ConstantExpression && + (node.initializer as ConstantExpression).constant + is NullConstant); + if ((node.initializer == null || hasImplicitInitializer) && + node.hasDeclaredInitializer) { + writeModifier(node.hasDeclaredInitializer, 'has-declared-initializer'); + } else if (node.initializer != null && + !hasImplicitInitializer && + !node.hasDeclaredInitializer) { + writeModifier( + node.hasDeclaredInitializer, 'has-no-declared-initializer'); + } + writeWord(getVariableName(node.variable)); + Expression? initializer = node.initializer; + if (initializer != null) { + writeSpaced(':='); + writeExpression(initializer); + } } } diff --git a/pkg/kernel/lib/type_checker.dart b/pkg/kernel/lib/type_checker.dart index 22018545227..53fa4b0800a 100644 --- a/pkg/kernel/lib/type_checker.dart +++ b/pkg/kernel/lib/type_checker.dart @@ -1023,7 +1023,7 @@ class TypeCheckingVisitor @override void visitForStatement(ForStatement node) { - node.variables.forEach(visitVariableDeclaration); + node.variableInitializations.forEach(visitVariableInitialization); if (node.condition != null) { node.condition = checkExpressionAndAssignability( node.condition!, environment.coreTypes.boolNonNullableRawType); @@ -1093,6 +1093,11 @@ class TypeCheckingVisitor @override void visitVariableDeclaration(VariableDeclaration node) { + visitVariableInitialization(node); + } + + @override + void visitVariableInitialization(VariableInitialization node) { if (node.initializer != null) { node.initializer = checkExpressionAndAssignability(node.initializer!, node.type); @@ -1347,13 +1352,6 @@ class TypeCheckingVisitor // TODO(johnniwinther): Implement this. } - @override - void visitVariableInitialization(VariableInitialization node) { - // TODO(cstefantsova): Implement visitVariableInitialization. - throw new UnimplementedError( - "Unimplemented support for $node (${node.runtimeType})."); - } - @override DartType visitVariableRead(VariableRead node) { // TODO(cstefantsova): Implement visitVariableRead. diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart index afa4d923fee..dbc03f5bc71 100644 --- a/pkg/kernel/lib/visitor.dart +++ b/pkg/kernel/lib/visitor.dart @@ -2474,6 +2474,16 @@ class RemovingTransformer extends TreeVisitor1Default { transformList(nodes, parent, dummyVariableDeclaration); } + /// Transforms or removes [VariableInitialization] nodes in [nodes] as + /// children of [parent]. + /// + /// This is convenience method for calling [transformList] with removal + /// sentinel for [VariableInitialization] 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.