Modify property promotion: only this
https://dart-review.googlesource.com/c/sdk/+/498840 added support for promotion of properties (private, final instance variables with a name which isn't used much for other purposes) in the context of anonymous methods. This CL reduces the set of situations where this feature is enabled such that only `this` will allow property promotions to be carried in (such that `this._x` is promoted in `v.=> this._x` when `v` is such that `v._x` has been promoted before the anonymous method occurs). It also generalizes the mechanism such that property promotions are carried out (so we can do `if (v.=> _x is int) v._x.isEven;`). Tests has been adjusted accordingly. Change-Id: Ibe70713d3d9c89a6d95f9c3dd28df8f147cb518d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/502660 Reviewed-by: Paul Berry <paulberry@google.com> Commit-Queue: Erik Ernst <eernst@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
3f66adee09
commit
96a4dd4c19
@@ -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'),
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1993,7 +1993,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
||||
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<void>
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,19 +56,20 @@ void testParameterlessPromotionsCarriedIn(C c) {
|
||||
}
|
||||
}
|
||||
|
||||
void testParameterfulPromotionsCarriedIn(C c) {
|
||||
void testParameterfulPromotionsNotCarriedIn(C c) {
|
||||
if (c._x != null) {
|
||||
c._x.expectStaticType<Exactly<Object>>;
|
||||
c.(p) {
|
||||
// `p` refers to the same object as `c`, so `p._x` is promoted.
|
||||
p._x.expectStaticType<Exactly<Object>>;
|
||||
// `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<Exactly<Object?>>;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
testParameterlessPromotionsCarriedIn(C(0));
|
||||
testParameterfulPromotionsCarriedIn(C(0));
|
||||
C(0).testParameterlessRebindsThis();
|
||||
C(0).testParameterfulDoesNotRebindThis();
|
||||
testParameterlessPromotionsCarriedIn(C(0));
|
||||
testParameterfulPromotionsNotCarriedIn(C(0));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class C {
|
||||
}
|
||||
}
|
||||
|
||||
void testParameterlessPromotionsCarriedIn(C c) {
|
||||
void testParameterlessPromotionsCarriedInAndOut(C c) {
|
||||
if (c._x != null) {
|
||||
c._x.expectStaticType<Exactly<Object>>;
|
||||
// Inside the anonymous method, `this` refers to the same object as `c`, so
|
||||
@@ -51,20 +51,29 @@ void testParameterlessPromotionsCarriedIn(C c) {
|
||||
c.=> _x.expectStaticType<Exactly<Object>>;
|
||||
c.=> this._x.expectStaticType<Exactly<Object>>;
|
||||
}
|
||||
// The promotion is carried out again.
|
||||
c._x.expectStaticType<Exactly<Object?>>;
|
||||
if (c.=> _x is Object) {
|
||||
c._x.expectStaticType<Exactly<Object>>;
|
||||
}
|
||||
if (c.=> this._x is Object) {
|
||||
c._x.expectStaticType<Exactly<Object>>;
|
||||
}
|
||||
}
|
||||
|
||||
void testParameterfulPromotionsCarriedIn(C c) {
|
||||
void testParameterfulPromotionsNotCarriedIn(C c) {
|
||||
if (c._x != null) {
|
||||
c._x.expectStaticType<Exactly<Object>>;
|
||||
// Inside the anonymous method, `p` refers to the same object as `c`, so
|
||||
// `p._x` is promoted.
|
||||
c.(p) => p._x.expectStaticType<Exactly<Object>>;
|
||||
// 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<Exactly<Object?>>;
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
testParameterlessPromotionsCarriedIn(C(0));
|
||||
testParameterfulPromotionsCarriedIn(C(0));
|
||||
C(0).testParameterlessRebindsThis();
|
||||
C(0).testParameterfulDoesNotRebindThis();
|
||||
testParameterlessPromotionsCarriedInAndOut(C(0));
|
||||
testParameterfulPromotionsNotCarriedIn(C(0));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user