Support promotion of instance variables with anonymous methods
This CL adds support for promotion of certain private final instance variables along with anonymous methods. The promotions do not differ from the ones which are already available in Dart without anonymous methods, but it requires some generalizations to handle the changing value of `this` which is made possible by anonymous methods. Change-Id: I720a5fa6d29a8a7d19bb2e167dc135f97492b525 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/498840 Reviewed-by: Paul Berry <paulberry@google.com>
This commit is contained in:
@@ -750,6 +750,7 @@ abstract class FlowAnalysis<
|
||||
required bool isFinal,
|
||||
required bool isLate,
|
||||
required bool isImplicitlyTyped,
|
||||
bool inheritPromotableProperties = false,
|
||||
});
|
||||
|
||||
/// Whether the [variable] is definitely assigned in the current state.
|
||||
@@ -1195,6 +1196,12 @@ abstract class FlowAnalysis<
|
||||
SharedTypeView scrutineeType,
|
||||
);
|
||||
|
||||
/// Call this method just before changing the binding of `this`.
|
||||
void thisBinding_begin(ExpressionInfo? targetInfo);
|
||||
|
||||
/// Call this method just after the end of a `this` binding.
|
||||
void thisBinding_end();
|
||||
|
||||
/// Call this method just after visiting the expression `this` (or the
|
||||
/// pseudo-expression `super`, in the case of the analyzer, which represents
|
||||
/// `super.x` as a property get whose target is `super`).
|
||||
@@ -1956,11 +1963,13 @@ class FlowAnalysisDebug<
|
||||
required bool isFinal,
|
||||
required bool isLate,
|
||||
required bool isImplicitlyTyped,
|
||||
bool inheritPromotableProperties = false,
|
||||
}) {
|
||||
_wrap(
|
||||
'initialize($variable, $matchedType, $initializerExpressionInfo, '
|
||||
'isFinal: $isFinal, isLate: $isLate, '
|
||||
'isImplicitlyTyped: $isImplicitlyTyped)',
|
||||
'isImplicitlyTyped: $isImplicitlyTyped, '
|
||||
'inheritPromotableProperties: $inheritPromotableProperties)',
|
||||
() => _wrapped.initialize(
|
||||
variable,
|
||||
matchedType,
|
||||
@@ -1968,6 +1977,7 @@ class FlowAnalysisDebug<
|
||||
isFinal: isFinal,
|
||||
isLate: isLate,
|
||||
isImplicitlyTyped: isImplicitlyTyped,
|
||||
inheritPromotableProperties: inheritPromotableProperties,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -2498,6 +2508,19 @@ class FlowAnalysisDebug<
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void thisBinding_begin(ExpressionInfo? targetInfo) {
|
||||
_wrap(
|
||||
'thisBinding_begin($targetInfo)',
|
||||
() => _wrapped.thisBinding_begin(targetInfo),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void thisBinding_end() {
|
||||
_wrap('thisBinding_end()', () => _wrapped.thisBinding_end());
|
||||
}
|
||||
|
||||
@override
|
||||
ExpressionInfo thisOrSuper(
|
||||
SharedTypeView staticType, {
|
||||
@@ -5173,8 +5196,10 @@ class _FlowAnalysisImpl<
|
||||
@override
|
||||
late final SsaNode _superSsaNode = new SsaNode();
|
||||
|
||||
final List<SsaNode> _thisSsaNodes = [new SsaNode()];
|
||||
|
||||
@override
|
||||
late final SsaNode _thisSsaNode = new SsaNode();
|
||||
SsaNode get _thisSsaNode => _thisSsaNodes.last;
|
||||
|
||||
@override
|
||||
final List<_Reference> _cascadeTargetStack = [];
|
||||
@@ -5845,6 +5870,7 @@ class _FlowAnalysisImpl<
|
||||
required bool isFinal,
|
||||
required bool isLate,
|
||||
required bool isImplicitlyTyped,
|
||||
bool inheritPromotableProperties = false,
|
||||
}) {
|
||||
SharedTypeView unpromotedType = operations.variableType(variable);
|
||||
int variableKey = promotionKeyStore.keyForVariable(variable);
|
||||
@@ -5856,6 +5882,7 @@ class _FlowAnalysisImpl<
|
||||
isLate: isLate,
|
||||
isImplicitlyTyped: isImplicitlyTyped,
|
||||
unpromotedType: unpromotedType,
|
||||
inheritPromotableProperties: inheritPromotableProperties,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6643,6 +6670,24 @@ class _FlowAnalysisImpl<
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void thisBinding_begin(ExpressionInfo? targetInfo) {
|
||||
_Reference? expressionReference = _getExpressionReference(targetInfo);
|
||||
SsaNode ssaNode =
|
||||
expressionReference?.ssaNode ??
|
||||
new SsaNode(
|
||||
conditionVariableState: targetInfo != null && targetInfo.isNonTrivial
|
||||
? targetInfo
|
||||
: null,
|
||||
);
|
||||
_thisSsaNodes.add(ssaNode);
|
||||
}
|
||||
|
||||
@override
|
||||
void thisBinding_end() {
|
||||
_thisSsaNodes.removeLast();
|
||||
}
|
||||
|
||||
@override
|
||||
ExpressionInfo thisOrSuper(
|
||||
SharedTypeView staticType, {
|
||||
@@ -7445,6 +7490,7 @@ class _FlowAnalysisImpl<
|
||||
required bool isLate,
|
||||
required bool isImplicitlyTyped,
|
||||
required SharedTypeView unpromotedType,
|
||||
bool inheritPromotableProperties = false,
|
||||
}) {
|
||||
if (isLate) {
|
||||
// Don't use expression info for late variables, since we don't know when
|
||||
@@ -7458,12 +7504,15 @@ class _FlowAnalysisImpl<
|
||||
// https://github.com/dart-lang/language/issues/1785.
|
||||
expressionInfo = null;
|
||||
}
|
||||
SsaNode newSsaNode = new SsaNode(
|
||||
conditionVariableState:
|
||||
expressionInfo != null && expressionInfo.isNonTrivial
|
||||
? expressionInfo
|
||||
: null,
|
||||
);
|
||||
SsaNode newSsaNode =
|
||||
inheritPromotableProperties && expressionInfo is _Reference
|
||||
? expressionInfo.ssaNode
|
||||
: new SsaNode(
|
||||
conditionVariableState:
|
||||
expressionInfo != null && expressionInfo.isNonTrivial
|
||||
? expressionInfo
|
||||
: null,
|
||||
);
|
||||
_current = _current.write(
|
||||
this,
|
||||
null,
|
||||
@@ -7731,16 +7780,23 @@ class _FlowAnalysisImpl<
|
||||
).restoreConditionVariableState(scrutineeInfo, this, _current);
|
||||
}
|
||||
|
||||
TrivialVariableReference _thisOrSuperReference(
|
||||
_Reference _thisOrSuperReference(
|
||||
SharedTypeView staticType, {
|
||||
required bool isSuper,
|
||||
}) => new TrivialVariableReference(
|
||||
promotionKey: promotionKeyStore.thisPromotionKey,
|
||||
model: _current,
|
||||
type: staticType,
|
||||
isThisOrSuper: true,
|
||||
ssaNode: isSuper ? _superSsaNode : _thisSsaNode,
|
||||
);
|
||||
}) {
|
||||
SsaNode ssaNode = isSuper ? _superSsaNode : _thisSsaNode;
|
||||
return new TrivialVariableReference(
|
||||
promotionKey: promotionKeyStore.thisPromotionKey,
|
||||
model: _current,
|
||||
type: staticType,
|
||||
isThisOrSuper: true,
|
||||
ssaNode: ssaNode,
|
||||
).restoreConditionVariableState(
|
||||
ssaNode.conditionVariableState,
|
||||
this,
|
||||
_current,
|
||||
);
|
||||
}
|
||||
|
||||
TrivialVariableReference _variableReference(
|
||||
int variableKey,
|
||||
|
||||
@@ -12827,6 +12827,165 @@ main() {
|
||||
checkAssigned(branch1, false),
|
||||
]);
|
||||
});
|
||||
|
||||
test('Anonymous method with target having no ExpressionInfo', () {
|
||||
h.run([
|
||||
expr(
|
||||
'A',
|
||||
).invokeAnonymousMethod([checkReachable(true)], returnType: 'void'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('Anonymous method has a different notion of this', () {
|
||||
h.addMember('C', '_field', 'Object', promotable: true);
|
||||
h.thisType = 'C';
|
||||
h.run([
|
||||
this_.as_('C'),
|
||||
this_.property('_field').as_('num'),
|
||||
checkPromoted(this_.property('_field'), 'num'),
|
||||
expr('C').invokeAnonymousMethod([
|
||||
checkNotPromoted(this_.property('_field')),
|
||||
this_.property('_field').as_('int'),
|
||||
checkPromoted(this_.property('_field'), 'int'),
|
||||
], returnType: 'void'),
|
||||
checkPromoted(this_.property('_field'), 'num'),
|
||||
]);
|
||||
});
|
||||
|
||||
test(
|
||||
'Anonymous method with target having ExpressionInfo but not a Reference',
|
||||
() {
|
||||
h.addMember('C', '_field', 'Object', promotable: true);
|
||||
h.thisType = 'C';
|
||||
h.run([
|
||||
this_.as_('C'),
|
||||
this_.property('_field').as_('num'),
|
||||
checkPromoted(this_.property('_field'), 'num'),
|
||||
expr('bool').conditional(expr('C'), expr('C')).invokeAnonymousMethod([
|
||||
checkNotPromoted(this_.property('_field')),
|
||||
this_.property('_field').as_('int'),
|
||||
checkPromoted(this_.property('_field'), 'int'),
|
||||
], returnType: 'void'),
|
||||
checkPromoted(this_.property('_field'), 'num'),
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
test('Anonymous method promotes this._field '
|
||||
'from localVariable._field and vice versa', () {
|
||||
var x = Var('x');
|
||||
h.addMember('A', '_field', 'Object', promotable: true);
|
||||
h.run([
|
||||
declare(x, type: 'A', initializer: expr('A')),
|
||||
x.property('_field').as_('num'),
|
||||
x.invokeAnonymousMethod([
|
||||
checkPromoted(this_.property('_field'), 'num'),
|
||||
this_.property('_field').as_('int'),
|
||||
], returnType: 'void'),
|
||||
checkPromoted(x.property('_field'), 'int'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('Anonymous method promotes this._field '
|
||||
'from instanceVariable._field and vice versa', () {
|
||||
h.addMember('A', '_field', 'Object', promotable: true);
|
||||
h.addMember('B', '_subField', 'Object', promotable: true);
|
||||
h.thisType = 'A';
|
||||
h.run([
|
||||
this_.as_('A'),
|
||||
this_.property('_field').as_('B'),
|
||||
this_.property('_field').property('_subField').as_('num'),
|
||||
this_.property('_field').invokeAnonymousMethod([
|
||||
checkPromoted(this_.property('_subField'), 'num'),
|
||||
this_.property('_subField').as_('int'),
|
||||
], returnType: 'void'),
|
||||
checkPromoted(this_.property('_field').property('_subField'), 'int'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('Parameterized anonymous method has the same notion of this', () {
|
||||
h.addMember('C', '_field', 'Object', promotable: true);
|
||||
h.thisType = 'C';
|
||||
h.run([
|
||||
this_.as_('C'),
|
||||
this_.property('_field').as_('num'),
|
||||
checkPromoted(this_.property('_field'), 'num'),
|
||||
expr('C').invokeAnonymousMethod(isParameterless: false, [
|
||||
checkPromoted(this_.property('_field'), 'num'),
|
||||
this_.property('_field').as_('int'),
|
||||
checkPromoted(this_.property('_field'), 'int'),
|
||||
], returnType: 'void'),
|
||||
checkPromoted(this_.property('_field'), 'int'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('Parameterized anonymous method with target having ExpressionInfo '
|
||||
'but not a Reference', () {
|
||||
h.addMember('C', '_field', 'Object', promotable: true);
|
||||
h.thisType = 'C';
|
||||
h.run([
|
||||
this_.as_('C'),
|
||||
this_.property('_field').as_('num'),
|
||||
checkPromoted(this_.property('_field'), 'num'),
|
||||
expr('bool')
|
||||
.conditional(expr('C'), expr('C'))
|
||||
.invokeAnonymousMethod(isParameterless: false, [
|
||||
checkPromoted(this_.property('_field'), 'num'),
|
||||
this_.property('_field').as_('int'),
|
||||
checkPromoted(this_.property('_field'), 'int'),
|
||||
], returnType: 'void'),
|
||||
checkPromoted(this_.property('_field'), 'int'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('Parameterized anonymous method parameter inherits promotion', () {
|
||||
var x = Var('x');
|
||||
var p = Var('p');
|
||||
h.addMember('A', '_field', 'Object', promotable: true);
|
||||
h.thisType = 'A';
|
||||
h.run([
|
||||
declare(x, type: 'A', initializer: expr('A')),
|
||||
x.property('_field').as_('num'),
|
||||
x.invokeAnonymousMethod(isParameterless: false, parameter: p, [
|
||||
checkPromoted(p.property('_field'), 'num'),
|
||||
p.property('_field').as_('int'),
|
||||
], returnType: 'void'),
|
||||
checkPromoted(x.property('_field'), 'int'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('Parameterized anonymous method promotes parameter._field '
|
||||
'from instanceVariable._field and vice versa', () {
|
||||
var p = Var('p');
|
||||
h.addMember('A', '_field', 'B', promotable: true);
|
||||
h.addMember('B', '_subField', 'Object', promotable: true);
|
||||
h.thisType = 'A';
|
||||
h.run([
|
||||
this_.as_('A'),
|
||||
this_.property('_field').as_('B'),
|
||||
this_.property('_field').property('_subField').as_('num'),
|
||||
this_
|
||||
.property('_field')
|
||||
.invokeAnonymousMethod(isParameterless: false, parameter: p, [
|
||||
checkPromoted(p.property('_subField'), 'num'),
|
||||
p.property('_subField').as_('int'),
|
||||
], returnType: 'void'),
|
||||
checkPromoted(this_.property('_field').property('_subField'), 'int'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('Anonymous method this serves as condition variable', () {
|
||||
var x = Var('x');
|
||||
h.run([
|
||||
declare(x, type: 'int?', initializer: expr('int?')),
|
||||
x.eq(nullLiteral).not.invokeAnonymousMethod(isParameterless: true, [
|
||||
this_.conditional(
|
||||
checkPromoted(x, 'int'),
|
||||
expr('bool'),
|
||||
),
|
||||
], returnType: 'bool'),
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2453,17 +2453,26 @@ class InvokeAnonymousMethod extends Expression {
|
||||
|
||||
final bool isNullAware;
|
||||
|
||||
final bool isParameterless;
|
||||
|
||||
final Var? parameter;
|
||||
|
||||
InvokeAnonymousMethod._(
|
||||
this.target,
|
||||
this.body, {
|
||||
required this.returnType,
|
||||
required this.isNullAware,
|
||||
required this.isParameterless,
|
||||
this.parameter,
|
||||
required super.location,
|
||||
});
|
||||
|
||||
@override
|
||||
void preVisit(PreVisitor visitor) {
|
||||
target.preVisit(visitor);
|
||||
if (parameter != null) {
|
||||
visitor._assignedVariables.declare(parameter!);
|
||||
}
|
||||
body.preVisit(visitor);
|
||||
}
|
||||
|
||||
@@ -2484,10 +2493,40 @@ class InvokeAnonymousMethod extends Expression {
|
||||
if (isNullAware) {
|
||||
targetResult = h.typeAnalyzer.createNullAwareGuard(target, targetResult);
|
||||
}
|
||||
var targetInfo = targetResult.flowAnalysisInfo;
|
||||
var previousThisType = h._thisType;
|
||||
if (isParameterless) {
|
||||
h.flow.thisBinding_begin(targetInfo);
|
||||
h._thisType = targetResult.type.unwrapTypeView();
|
||||
}
|
||||
h.flow.anonymousBlockBody_begin();
|
||||
if (parameter != null) {
|
||||
bool isImplicitlyTyped = parameter!._type == null;
|
||||
if (parameter!._type == null) {
|
||||
parameter!._type = targetResult.type.unwrapTypeView<Type>();
|
||||
}
|
||||
h.flow.declare(
|
||||
parameter!,
|
||||
SharedTypeView(parameter!.type),
|
||||
initialized: false,
|
||||
);
|
||||
h.flow.initialize(
|
||||
parameter!,
|
||||
targetResult.type,
|
||||
targetInfo,
|
||||
isFinal: false,
|
||||
isLate: false,
|
||||
isImplicitlyTyped: isImplicitlyTyped,
|
||||
inheritPromotableProperties: true,
|
||||
);
|
||||
}
|
||||
// Analyze the block, and generate its IR.
|
||||
body.visit(h);
|
||||
h.flow.anonymousBlockBody_end();
|
||||
if (isParameterless) {
|
||||
h._thisType = previousThisType;
|
||||
h.flow.thisBinding_end();
|
||||
}
|
||||
// Form the IR for the anonymous method invocation.
|
||||
h.irBuilder.apply(
|
||||
'anonymous-method',
|
||||
@@ -4971,6 +5010,8 @@ mixin ProtoExpression
|
||||
List<ProtoStatement> body, {
|
||||
required String returnType,
|
||||
bool isNullAware = false,
|
||||
bool isParameterless = true,
|
||||
Var? parameter,
|
||||
}) {
|
||||
var location = computeLocation();
|
||||
return new InvokeAnonymousMethod._(
|
||||
@@ -4978,6 +5019,8 @@ mixin ProtoExpression
|
||||
Block._(body, location: location),
|
||||
returnType: Type(returnType),
|
||||
isNullAware: isNullAware,
|
||||
isParameterless: isParameterless,
|
||||
parameter: parameter,
|
||||
location: location,
|
||||
);
|
||||
}
|
||||
@@ -7129,6 +7172,7 @@ class _MiniAstTypeAnalyzer
|
||||
SharedTypeView(thisType),
|
||||
isSuper: false,
|
||||
);
|
||||
flow.storeExpressionInfo(node, flowAnalysisInfo);
|
||||
return new ExpressionTypeAnalysisResult(
|
||||
type: SharedTypeView(thisType),
|
||||
flowAnalysisInfo: flowAnalysisInfo,
|
||||
@@ -7151,6 +7195,9 @@ class _MiniAstTypeAnalyzer
|
||||
SharedTypeView(memberType),
|
||||
);
|
||||
var promotedType = wrappedPromotedType?.unwrapTypeView();
|
||||
if (flowAnalysisInfo != null) {
|
||||
flow.storeExpressionInfo(node, flowAnalysisInfo);
|
||||
}
|
||||
return new ExpressionTypeAnalysisResult(
|
||||
type: SharedTypeView(promotedType ?? memberType),
|
||||
flowAnalysisInfo: flowAnalysisInfo,
|
||||
@@ -7249,6 +7296,7 @@ class _MiniAstTypeAnalyzer
|
||||
) {
|
||||
var (promotedType, flowAnalysisInfo) = flow.variableRead(variable);
|
||||
callback?.call(promotedType?.unwrapTypeView());
|
||||
flow.storeExpressionInfo(node, flowAnalysisInfo);
|
||||
return new ExpressionTypeAnalysisResult(
|
||||
type: promotedType ?? SharedTypeView(variable.type),
|
||||
flowAnalysisInfo: flowAnalysisInfo,
|
||||
@@ -7843,6 +7891,9 @@ class _MiniAstTypeAnalyzer
|
||||
member,
|
||||
SharedTypeView(memberType),
|
||||
);
|
||||
if (propertyGetNode != null && flowAnalysisInfo != null) {
|
||||
flow.storeExpressionInfo(propertyGetNode, flowAnalysisInfo);
|
||||
}
|
||||
return ExpressionTypeAnalysisResult(
|
||||
type: wrappedPromotedType ?? SharedTypeView(memberType),
|
||||
flowAnalysisInfo: flowAnalysisInfo,
|
||||
|
||||
@@ -1904,6 +1904,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
||||
node.visitChildren(this);
|
||||
var returnType = _finishFunctionBodyInference();
|
||||
flowAnalysis.flow?.anonymousBlockBody_end();
|
||||
|
||||
return returnType;
|
||||
} finally {
|
||||
_bodyContext = oldBodyContext;
|
||||
@@ -1916,11 +1917,13 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
||||
TypeImpl? imposedType,
|
||||
}) {
|
||||
checkUnreachableNode(node);
|
||||
|
||||
analyzeExpression(
|
||||
node.expression,
|
||||
SharedTypeSchemaView(imposedType ?? UnknownInferredType.instance),
|
||||
);
|
||||
popRewrite();
|
||||
|
||||
return node.expression.staticType ?? typeProvider.dynamicType;
|
||||
}
|
||||
|
||||
@@ -1975,11 +1978,33 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
||||
for (var parameter in parameters.parameters) {
|
||||
var element = parameter.declaredFragment?.element;
|
||||
if (element != null) {
|
||||
flow.declare(
|
||||
element,
|
||||
SharedTypeView(element.type),
|
||||
initialized: true,
|
||||
);
|
||||
if (parameter == parameters.parameters.first) {
|
||||
flow.declare(
|
||||
element,
|
||||
SharedTypeView(element.type),
|
||||
initialized: false,
|
||||
);
|
||||
flow.initialize(
|
||||
element,
|
||||
SharedTypeView(element.type),
|
||||
target != null
|
||||
? flowAnalysis.flow?.getExpressionInfo(target)
|
||||
: null,
|
||||
isFinal: false,
|
||||
isLate: false,
|
||||
isImplicitlyTyped: parameter.type == null,
|
||||
inheritPromotableProperties: true,
|
||||
);
|
||||
} else {
|
||||
// An error will occur because there are multiple parameters, but
|
||||
// those extra parameters should still allow for meaningful analysis
|
||||
// in the body of the anonymous method.
|
||||
flow.declare(
|
||||
element,
|
||||
SharedTypeView(element.type),
|
||||
initialized: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1988,9 +2013,15 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
||||
if (parameters == null) {
|
||||
var oldThisType = _thisType;
|
||||
_thisType = parameterType;
|
||||
var target = node.target;
|
||||
var targetInfo = target != null
|
||||
? flowAnalysis.flow?.getExpressionInfo(target)
|
||||
: null;
|
||||
flowAnalysis.flow?.thisBinding_begin(targetInfo);
|
||||
try {
|
||||
returnedType = node.body.resolve(this, contextType);
|
||||
} finally {
|
||||
flowAnalysis.flow?.thisBinding_end();
|
||||
_thisType = oldThisType;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -11868,7 +11868,16 @@ class InferenceVisitorImpl extends InferenceVisitorBase
|
||||
flowAnalysis.declare(
|
||||
node.variable,
|
||||
new SharedTypeView(node.variable.type),
|
||||
initialized: true,
|
||||
initialized: false,
|
||||
);
|
||||
flowAnalysis.initialize(
|
||||
node.variable,
|
||||
new SharedTypeView(node.variable.type),
|
||||
flowAnalysis.getExpressionInfo(node.variable.initializer!),
|
||||
isFinal: false,
|
||||
isLate: false,
|
||||
isImplicitlyTyped: node.isImplicitlyTyped,
|
||||
inheritPromotableProperties: true,
|
||||
);
|
||||
if (node.isNullAware) {
|
||||
flow.nullAwareAccess_rightBegin(
|
||||
@@ -11878,11 +11887,20 @@ class InferenceVisitorImpl extends InferenceVisitorBase
|
||||
);
|
||||
}
|
||||
|
||||
bool isParameterless = node.variable.isSynthesized;
|
||||
if (isParameterless) {
|
||||
flow.thisBinding_begin(
|
||||
flowAnalysis.getExpressionInfo(node.variable.initializer!),
|
||||
);
|
||||
}
|
||||
ExpressionInferenceResult bodyResult = inferExpression(
|
||||
node.body,
|
||||
typeContext,
|
||||
isVoidAllowed: true,
|
||||
);
|
||||
if (isParameterless) {
|
||||
flow.thisBinding_end();
|
||||
}
|
||||
|
||||
if (node.isNullAware) {
|
||||
flow.nullAwareAccess_end();
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// SharedOptions=--enable-experiment=anonymous-methods
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
void main() {
|
||||
final int? x = 2;
|
||||
(x != null).{
|
||||
Expect.isTrue(this ? x.isEven : false);
|
||||
};
|
||||
(x != null).(b) {
|
||||
Expect.isTrue(b ? x.isEven : false);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// SharedOptions=--enable-experiment=anonymous-methods
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
// SharedOptions=--enable-experiment=anonymous-methods
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import '../../static_type_helper.dart';
|
||||
|
||||
extension on int {
|
||||
int get g1 => this + '$this'.=> this.length;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
// SharedOptions=--enable-experiment=anonymous-methods
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import '../../static_type_helper.dart';
|
||||
|
||||
class A {
|
||||
final int x = 'first'.=> this.length + length;
|
||||
|
||||
Reference in New Issue
Block a user