diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart index 592e526defa..16cadcc75bb 100644 --- a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart +++ b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart @@ -12938,7 +12938,8 @@ main() { ]); }); - test('Parameterized anonymous method parameter inherits promotion', () { + test('Parameterized anonymous method parameter ' + 'does not inherit promotion', () { var x = Var('x'); var p = Var('p'); h.addMember('A', '_field', 'Object', promotable: true); @@ -12947,15 +12948,15 @@ main() { declare(x, type: 'A', initializer: expr('A')), x.property('_field').as_('num'), x.invokeAnonymousMethod(isParameterless: false, parameter: p, [ - checkPromoted(p.property('_field'), 'num'), + checkNotPromoted(p.property('_field')), p.property('_field').as_('int'), ], returnType: 'void'), - checkPromoted(x.property('_field'), 'int'), + checkPromoted(x.property('_field'), 'num'), ]); }); - test('Parameterized anonymous method promotes parameter._field ' - 'from instanceVariable._field and vice versa', () { + test('Parameterized anonymous method does not promote parameter._field ' + 'from instanceVariable._field or vice versa', () { var p = Var('p'); h.addMember('A', '_field', 'B', promotable: true); h.addMember('B', '_subField', 'Object', promotable: true); @@ -12967,10 +12968,10 @@ main() { this_ .property('_field') .invokeAnonymousMethod(isParameterless: false, parameter: p, [ - checkPromoted(p.property('_subField'), 'num'), + checkNotPromoted(p.property('_subField')), p.property('_subField').as_('int'), ], returnType: 'void'), - checkPromoted(this_.property('_field').property('_subField'), 'int'), + checkPromoted(this_.property('_field').property('_subField'), 'num'), ]); }); diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart index 26c3c13e958..d4c9632a023 100644 --- a/pkg/_fe_analyzer_shared/test/mini_ast.dart +++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart @@ -2517,7 +2517,7 @@ class InvokeAnonymousMethod extends Expression { isFinal: false, isLate: false, isImplicitlyTyped: isImplicitlyTyped, - inheritPromotableProperties: true, + inheritPromotableProperties: isParameterless, ); } // Analyze the block, and generate its IR. diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart index b741b531d08..afc8f2fb1d6 100644 --- a/pkg/analyzer/lib/src/generated/resolver.dart +++ b/pkg/analyzer/lib/src/generated/resolver.dart @@ -1993,7 +1993,7 @@ class ResolverVisitor extends ThrowingAstVisitor isFinal: false, isLate: false, isImplicitlyTyped: parameter.type == null, - inheritPromotableProperties: true, + inheritPromotableProperties: false, ); } else { // An error will occur because there are multiple parameters, but @@ -2017,13 +2017,20 @@ class ResolverVisitor extends ThrowingAstVisitor var targetInfo = target != null ? flowAnalysis.flow?.getExpressionInfo(target) : null; + var body = node.body; flowAnalysis.flow?.thisBinding_begin(targetInfo); try { - returnedType = node.body.resolve(this, contextType); + returnedType = body.resolve(this, contextType); } finally { flowAnalysis.flow?.thisBinding_end(); _thisType = oldThisType; } + if (body is AnonymousExpressionBodyImpl) { + flowAnalysis.flow?.storeExpressionInfo( + node, + flowAnalysis.flow?.getExpressionInfo(body.expression), + ); + } } else { returnedType = node.body.resolve(this, contextType); } diff --git a/pkg/front_end/lib/src/kernel/internal_ast.dart b/pkg/front_end/lib/src/kernel/internal_ast.dart index e723a22b460..1993c30f828 100644 --- a/pkg/front_end/lib/src/kernel/internal_ast.dart +++ b/pkg/front_end/lib/src/kernel/internal_ast.dart @@ -516,9 +516,10 @@ class Cascade extends InternalExpression { class AnonymousMethodExpression extends InternalExpression { VariableDeclaration variable; Expression body; + final bool isCascade; final bool isImplicitlyTyped; final bool isNullAware; - final bool isCascade; + final bool isParameterless; final int typeOffset; AnonymousMethodExpression( @@ -528,7 +529,7 @@ class AnonymousMethodExpression extends InternalExpression { required this.isNullAware, required this.isCascade, required this.typeOffset, - }) { + }) : isParameterless = variable.isSynthesized { variable.parent = this; body.parent = this; } 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 09af5a8a16a..9adc088ebb4 100644 --- a/pkg/front_end/lib/src/type_inference/inference_visitor.dart +++ b/pkg/front_end/lib/src/type_inference/inference_visitor.dart @@ -11881,7 +11881,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase isFinal: false, isLate: false, isImplicitlyTyped: node.isImplicitlyTyped, - inheritPromotableProperties: true, + inheritPromotableProperties: node.isParameterless, ); if (node.isNullAware) { flow.nullAwareAccess_rightBegin( @@ -11891,8 +11891,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase ); } - bool isParameterless = node.variable.isSynthesized; - if (isParameterless) { + if (node.isParameterless) { flow.thisBinding_begin( flowAnalysis.getExpressionInfo(node.variable.initializer!), ); @@ -11902,7 +11901,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase typeContext, isVoidAllowed: true, ); - if (isParameterless) { + if (node.isParameterless) { flow.thisBinding_end(); } @@ -11966,6 +11965,13 @@ class InferenceVisitorImpl extends InferenceVisitorBase replacement = new Let(node.variable, body)..fileOffset = node.fileOffset; } + if (node.isParameterless) { + flowAnalysis.storeExpressionInfo( + replacement, + flowAnalysis.getExpressionInfo(node.body), + ); + } + return new ExpressionInferenceResult(inferredType, replacement); } diff --git a/tests/language/anonymous_methods/block/private_field_promotion_test.dart b/tests/language/anonymous_methods/block/private_field_promotion_test.dart index b31bd7dbb14..2589b89937f 100644 --- a/tests/language/anonymous_methods/block/private_field_promotion_test.dart +++ b/tests/language/anonymous_methods/block/private_field_promotion_test.dart @@ -56,19 +56,20 @@ void testParameterlessPromotionsCarriedIn(C c) { } } -void testParameterfulPromotionsCarriedIn(C c) { +void testParameterfulPromotionsNotCarriedIn(C c) { if (c._x != null) { c._x.expectStaticType>; c.(p) { - // `p` refers to the same object as `c`, so `p._x` is promoted. - p._x.expectStaticType>; + // `p` refers to the same object as `c`, but `p._x` is not promoted + // because `p` is treated like a normal local variable. + p._x.expectStaticType>; }; } } main() { - testParameterlessPromotionsCarriedIn(C(0)); - testParameterfulPromotionsCarriedIn(C(0)); C(0).testParameterlessRebindsThis(); C(0).testParameterfulDoesNotRebindThis(); + testParameterlessPromotionsCarriedIn(C(0)); + testParameterfulPromotionsNotCarriedIn(C(0)); } diff --git a/tests/language/anonymous_methods/expression/condition_variable_test.dart b/tests/language/anonymous_methods/expression/condition_variable_test.dart index ddbc1dc421f..6c5f0e92ea5 100644 --- a/tests/language/anonymous_methods/expression/condition_variable_test.dart +++ b/tests/language/anonymous_methods/expression/condition_variable_test.dart @@ -9,5 +9,5 @@ import 'package:expect/expect.dart'; void main() { final int? x = 2; Expect.isTrue((x != null).=> this ? x.isEven : false); - Expect.isTrue((x != null).(b) => b ? x.isEven : false); + Expect.isTrue((x != null).(p) => p ? x.isEven : false); } diff --git a/tests/language/anonymous_methods/expression/private_field_promotion_test.dart b/tests/language/anonymous_methods/expression/private_field_promotion_test.dart index 418adf9c0a0..81e61599c06 100644 --- a/tests/language/anonymous_methods/expression/private_field_promotion_test.dart +++ b/tests/language/anonymous_methods/expression/private_field_promotion_test.dart @@ -43,7 +43,7 @@ class C { } } -void testParameterlessPromotionsCarriedIn(C c) { +void testParameterlessPromotionsCarriedInAndOut(C c) { if (c._x != null) { c._x.expectStaticType>; // Inside the anonymous method, `this` refers to the same object as `c`, so @@ -51,20 +51,29 @@ void testParameterlessPromotionsCarriedIn(C c) { c.=> _x.expectStaticType>; c.=> this._x.expectStaticType>; } + // The promotion is carried out again. + c._x.expectStaticType>; + if (c.=> _x is Object) { + c._x.expectStaticType>; + } + if (c.=> this._x is Object) { + c._x.expectStaticType>; + } } -void testParameterfulPromotionsCarriedIn(C c) { +void testParameterfulPromotionsNotCarriedIn(C c) { if (c._x != null) { c._x.expectStaticType>; - // Inside the anonymous method, `p` refers to the same object as `c`, so - // `p._x` is promoted. - c.(p) => p._x.expectStaticType>; + // Inside the anonymous method, `p` refers to the same object as `c`, but + // `p._x` is not promoted because `p` is treated like a normal local + // variable. + c.(p) => p._x.expectStaticType>; } } main() { - testParameterlessPromotionsCarriedIn(C(0)); - testParameterfulPromotionsCarriedIn(C(0)); C(0).testParameterlessRebindsThis(); C(0).testParameterfulDoesNotRebindThis(); + testParameterlessPromotionsCarriedInAndOut(C(0)); + testParameterfulPromotionsNotCarriedIn(C(0)); }