Version 2.13.0-208.0.dev
Merge commit 'fc349bdbb5f9c6d4680c11858459abcb96670227' into 'dev'
diff --git a/DEPS b/DEPS
index f2572e7..2e25552 100644
--- a/DEPS
+++ b/DEPS
@@ -45,7 +45,7 @@
# hashes. It requires access to the dart-build-access group, which EngProd
# has.
"co19_rev": "fddb1dce948cec277bf3dc23b45ee95e761b89fe",
- "co19_2_rev": "6b71fed8c0f6cf396c085ba2d405d5a9af1daf45",
+ "co19_2_rev": "3642f24e2e6273c6fb65a8f60cd0edc95153942e",
# The internal benchmarks to use. See go/dart-benchmarks-internal
"benchmarks_internal_rev": "076df10d9b77af337f2d8029725787155eb1cd52",
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index e4c7261..bf501a0 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -736,8 +736,13 @@
/// expression to the left hand side of the `.`, and [propertyName] should be
/// the identifier to the right hand side of the `.`. [staticType] should be
/// the static type of the value returned by the property get.
+ ///
+ /// [propertyMember] should be whatever data structure the client uses to keep
+ /// track of the field or property being accessed. In the event of
+ /// non-promotion of a property get, this value can be retrieved from
+ /// [PropertyNotPromoted.propertyMember].
void propertyGet(Expression wholeExpression, Expression target,
- String propertyName, Type staticType);
+ String propertyName, Object? propertyMember, Type staticType);
/// Retrieves the SSA node associated with [variable], or `null` if [variable]
/// is not associated with an SSA node because it is write captured. For
@@ -788,8 +793,13 @@
/// the whole property get, and [propertyName] should be the name of the
/// property being read. [staticType] should be the static type of the value
/// returned by the property get.
- void thisOrSuperPropertyGet(
- Expression expression, String propertyName, Type staticType);
+ ///
+ /// [propertyMember] should be whatever data structure the client uses to keep
+ /// track of the field or property being accessed. In the event of
+ /// non-promotion of a property get, this value can be retrieved from
+ /// [PropertyNotPromoted.propertyMember].
+ void thisOrSuperPropertyGet(Expression expression, String propertyName,
+ Object? propertyMember, Type staticType);
/// Call this method just before visiting the body of a "try/catch" statement.
///
@@ -1339,11 +1349,12 @@
@override
void propertyGet(Expression wholeExpression, Expression target,
- String propertyName, Type staticType) {
+ String propertyName, Object? propertyMember, Type staticType) {
_wrap(
- 'propertyGet($wholeExpression, $target, $propertyName, $staticType)',
+ 'propertyGet($wholeExpression, $target, $propertyName, '
+ '$propertyMember, $staticType)',
() => _wrapped.propertyGet(
- wholeExpression, target, propertyName, staticType));
+ wholeExpression, target, propertyName, propertyMember, staticType));
}
@override
@@ -1378,12 +1389,13 @@
}
@override
- void thisOrSuperPropertyGet(
- Expression expression, String propertyName, Type staticType) {
+ void thisOrSuperPropertyGet(Expression expression, String propertyName,
+ Object? propertyMember, Type staticType) {
_wrap(
- 'thisOrSuperPropertyGet($expression, $propertyName, $staticType)',
+ 'thisOrSuperPropertyGet($expression, $propertyName, $propertyMember, '
+ '$staticType)',
() => _wrapped.thisOrSuperPropertyGet(
- expression, propertyName, staticType));
+ expression, propertyName, propertyMember, staticType));
}
@override
@@ -2334,12 +2346,17 @@
/// The name of the property.
final String propertyName;
+ /// The field or property being accessed. This matches a `propertyMember`
+ /// value that was passed to either [FlowAnalysis.propertyGet] or
+ /// [FlowAnalysis.thisOrSuperPropertyGet].
+ final Object? propertyMember;
+
/// The static type of the property at the time of the access. This is the
/// type that was passed to [FlowAnalysis.whyNotPromoted]; it is provided to
/// the client as a convenience for ID testing.
final Type staticType;
- PropertyNotPromoted(this.propertyName, this.staticType);
+ PropertyNotPromoted(this.propertyName, this.propertyMember, this.staticType);
@override
String get documentationLink => 'http://dart.dev/go/non-promo-property';
@@ -2517,8 +2534,10 @@
/// Creates a reference representing a get of a property called [propertyName]
/// on the reference represented by `this`.
- Reference<Variable, Type> propertyGet(String propertyName) =>
- new _PropertyGetReference<Variable, Type>(this, propertyName);
+ Reference<Variable, Type> propertyGet(
+ String propertyName, Object? propertyMember) =>
+ new _PropertyGetReference<Variable, Type>(
+ this, propertyName, propertyMember);
/// Stores info for this reference in [variableInfo].
void storeInfo(Map<Variable?, VariableModel<Variable, Type>> variableInfo,
@@ -3952,14 +3971,14 @@
@override
void propertyGet(Expression wholeExpression, Expression target,
- String propertyName, Type staticType) {
+ String propertyName, Object? propertyMember, Type staticType) {
Reference<Variable, Type>? reference =
_getExpressionReference(target)?.reference;
if (reference != null) {
_storeExpressionReference(
wholeExpression,
new ReferenceWithType<Variable, Type>(
- reference.propertyGet(propertyName), staticType));
+ reference.propertyGet(propertyName, propertyMember), staticType));
}
}
@@ -4016,12 +4035,13 @@
}
@override
- void thisOrSuperPropertyGet(
- Expression expression, String propertyName, Type staticType) {
+ void thisOrSuperPropertyGet(Expression expression, String propertyName,
+ Object? propertyMember, Type staticType) {
_storeExpressionReference(
expression,
new ReferenceWithType<Variable, Type>(
- new _ThisReference<Variable, Type>().propertyGet(propertyName),
+ new _ThisReference<Variable, Type>()
+ .propertyGet(propertyName, propertyMember),
staticType));
}
@@ -4686,7 +4706,7 @@
@override
void propertyGet(Expression wholeExpression, Expression target,
- String propertyName, Type staticType) {}
+ String propertyName, Object? propertyMember, Type staticType) {}
@override
SsaNode<Variable, Type>? ssaNodeForTesting(Variable variable) {
@@ -4706,8 +4726,8 @@
void thisOrSuper(Expression expression, Type staticType) {}
@override
- void thisOrSuperPropertyGet(
- Expression expression, String propertyName, Type staticType) {}
+ void thisOrSuperPropertyGet(Expression expression, String propertyName,
+ Object? propertyMember, Type staticType) {}
@override
void tryCatchStatement_bodyBegin() {}
@@ -4908,7 +4928,12 @@
/// The name of the property.
final String propertyName;
- _PropertyGetReference(this.target, this.propertyName);
+ /// The field or property being accessed. This matches a `propertyMember`
+ /// value that was passed to either [FlowAnalysis.propertyGet] or
+ /// [FlowAnalysis.thisOrSuperPropertyGet].
+ final Object? propertyMember;
+
+ _PropertyGetReference(this.target, this.propertyName, this.propertyMember);
@override
Map<Type, NonPromotionReason> Function() getNonPromotionReasons(
@@ -4920,7 +4945,8 @@
return () {
Map<Type, NonPromotionReason> result = <Type, NonPromotionReason>{};
for (Type type in promotedTypes) {
- result[type] = new PropertyNotPromoted(propertyName, staticType);
+ result[type] =
+ new PropertyNotPromoted(propertyName, propertyMember, staticType);
}
return result;
};
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index 5e41049..fd0db4b 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -1510,7 +1510,7 @@
Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
var targetType = target._visit(h, flow);
var propertyType = h.getMember(targetType, propertyName);
- flow.propertyGet(this, target, propertyName, propertyType);
+ flow.propertyGet(this, target, propertyName, propertyName, propertyType);
return propertyType;
}
@@ -1614,7 +1614,7 @@
@override
Type _visit(
Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
- flow.thisOrSuperPropertyGet(this, propertyName, type);
+ flow.thisOrSuperPropertyGet(this, propertyName, propertyName, type);
return type;
}
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
index aaaa182..3b63d6c 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -121,8 +121,7 @@
CompileTimeErrorCode.INVALID_ASSIGNMENT,
right,
[rightType, writeType],
- _resolver.computeWhyNotPromotedMessages(
- right, right, whyNotPromoted?.call()),
+ _resolver.computeWhyNotPromotedMessages(right, whyNotPromoted?.call()),
);
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index caff683..a6ab6e7 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -807,7 +807,11 @@
);
}
_resolver.flowAnalysis?.flow?.propertyGet(
- functionExpression, target, node.methodName.name, getterReturnType);
+ functionExpression,
+ target,
+ node.methodName.name,
+ node.methodName.staticElement,
+ getterReturnType);
functionExpression.staticType = targetType;
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
index 8a64758..e2d5336 100644
--- a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
@@ -203,8 +203,8 @@
readElementRequested = readLookup.requested;
if (readElementRequested is PropertyAccessorElement &&
!readElementRequested.isStatic) {
- _resolver.flowAnalysis?.flow?.thisOrSuperPropertyGet(
- node, node.name, readElementRequested.returnType);
+ _resolver.flowAnalysis?.flow?.thisOrSuperPropertyGet(node, node.name,
+ readElementRequested, readElementRequested.returnType);
}
_resolver.checkReadOfNotAssignedLocalVariable(node, readElementRequested);
}
@@ -373,7 +373,11 @@
nameErrorEntity: propertyName,
);
- _resolver.flowAnalysis?.flow?.propertyGet(node, target, propertyName.name,
+ _resolver.flowAnalysis?.flow?.propertyGet(
+ node,
+ target,
+ propertyName.name,
+ result.getter,
result.getter?.returnType ?? _typeSystem.typeProvider.dynamicType);
if (hasRead && result.needsGetterError) {
@@ -653,6 +657,7 @@
node,
target,
propertyName.name,
+ readElement,
readElement?.returnType ?? _typeSystem.typeProvider.dynamicType);
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
index cff8b7e..4ffbaaa 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
@@ -130,11 +130,11 @@
if (flow != null) {
if (receiver != null) {
messages = _resolver.computeWhyNotPromotedMessages(
- receiver, nameErrorEntity, flow.whyNotPromoted(receiver)());
+ nameErrorEntity, flow.whyNotPromoted(receiver)());
} else {
var thisType = _resolver.thisType;
if (thisType != null) {
- messages = _resolver.computeWhyNotPromotedMessages(receiver,
+ messages = _resolver.computeWhyNotPromotedMessages(
nameErrorEntity, flow.whyNotPromotedImplicitThis(thisType)());
}
}
diff --git a/pkg/analyzer/lib/src/error/bool_expression_verifier.dart b/pkg/analyzer/lib/src/error/bool_expression_verifier.dart
index 43d2462..e56e3ad 100644
--- a/pkg/analyzer/lib/src/error/bool_expression_verifier.dart
+++ b/pkg/analyzer/lib/src/error/bool_expression_verifier.dart
@@ -57,7 +57,7 @@
errorCode: CompileTimeErrorCode
.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_CONDITION,
messages: _resolver.computeWhyNotPromotedMessages(
- expression, expression, whyNotPromoted?.call()));
+ expression, whyNotPromoted?.call()));
} else {
_errorReporter.reportErrorForNode(errorCode, expression, arguments);
}
diff --git a/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart b/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
index cf1f3b5..fb96d1b 100644
--- a/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
+++ b/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
@@ -76,8 +76,8 @@
List<DiagnosticMessage>? messages;
if (errorNode is Expression) {
- messages = _resolver.computeWhyNotPromotedMessages(errorNode, errorNode,
- _resolver.flowAnalysis?.flow?.whyNotPromoted(errorNode)());
+ messages = _resolver.computeWhyNotPromotedMessages(
+ errorNode, _resolver.flowAnalysis?.flow?.whyNotPromoted(errorNode)());
}
report(errorNode, receiverType, errorCode: errorCode, messages: messages);
return true;
diff --git a/pkg/analyzer/lib/src/generated/error_detection_helpers.dart b/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
index af745c8..ce69393 100644
--- a/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
+++ b/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
@@ -85,8 +85,8 @@
}
return;
}
- var messages = computeWhyNotPromotedMessages(
- expression, expression, whyNotPromoted?.call());
+ var messages =
+ computeWhyNotPromotedMessages(expression, whyNotPromoted?.call());
// report problem
if (isConstConstructor) {
// TODO(paulberry): this error should be based on the actual type of the
@@ -229,7 +229,6 @@
/// [whyNotPromoted] should be the non-promotion details returned by the flow
/// analysis engine.
List<DiagnosticMessage> computeWhyNotPromotedMessages(
- Expression? expression,
SyntacticEntity errorEntity,
Map<DartType, NonPromotionReason>? whyNotPromoted);
@@ -313,8 +312,7 @@
errorCode,
getErrorNode(expression),
[actualStaticType, expectedStaticType],
- computeWhyNotPromotedMessages(
- expression, expression, whyNotPromoted?.call()),
+ computeWhyNotPromotedMessages(expression, whyNotPromoted?.call()),
);
return false;
}
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 9601c2e9..0d097e9 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -312,7 +312,6 @@
@override
List<DiagnosticMessage> computeWhyNotPromotedMessages(
- Expression? expression,
SyntacticEntity errorEntity,
Map<DartType, NonPromotionReason>? whyNotPromoted) {
return [];
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 1a1918d..b878793 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -533,17 +533,13 @@
@override
List<DiagnosticMessage> computeWhyNotPromotedMessages(
- Expression? expression,
SyntacticEntity errorEntity,
Map<DartType, NonPromotionReason>? whyNotPromoted) {
- if (expression is NamedExpression) {
- expression = expression.expression;
- }
List<DiagnosticMessage> messages = [];
if (whyNotPromoted != null) {
for (var entry in whyNotPromoted.entries) {
var whyNotPromotedVisitor = _WhyNotPromotedVisitor(
- source, expression, errorEntity, flowAnalysis!.dataForTesting);
+ source, errorEntity, flowAnalysis!.dataForTesting);
if (typeSystem.isPotentiallyNullable(entry.key)) continue;
var message = entry.value.accept(whyNotPromotedVisitor);
if (message != null) {
@@ -3447,10 +3443,6 @@
PromotableElement, DartType> {
final Source source;
- /// The expression that was not promoted, or `null` if the thing that was not
- /// promoted was an implicit `this`.
- final Expression? _expression;
-
final SyntacticEntity _errorEntity;
final FlowAnalysisDataForTesting? _dataForTesting;
@@ -3459,8 +3451,7 @@
DartType? propertyType;
- _WhyNotPromotedVisitor(
- this.source, this._expression, this._errorEntity, this._dataForTesting);
+ _WhyNotPromotedVisitor(this.source, this._errorEntity, this._dataForTesting);
@override
DiagnosticMessage? visitDemoteViaExplicitWrite(
@@ -3480,18 +3471,7 @@
@override
DiagnosticMessage? visitPropertyNotPromoted(
PropertyNotPromoted<DartType> reason) {
- var expression = _expression;
- Element? receiverElement;
- if (expression is SimpleIdentifier) {
- receiverElement = expression.staticElement;
- } else if (expression is PropertyAccess) {
- receiverElement = expression.propertyName.staticElement;
- } else if (expression is PrefixedIdentifier) {
- receiverElement = expression.identifier.staticElement;
- } else {
- assert(false,
- 'Unrecognized property access expression: ${expression.runtimeType}');
- }
+ var receiverElement = reason.propertyMember;
if (receiverElement is PropertyAccessorElement) {
propertyReference = receiverElement;
propertyType = reason.staticType;
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index f6928ab..81f1761 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -414,7 +414,8 @@
assert(graph.isValid(), 'Graph not valid after ${phase.name}');
}
- runPhase(new SsaInstructionSelection(_options, _closedWorld));
+ runPhase(
+ new SsaInstructionSelection(_options, _closedWorld, _interceptorData));
runPhase(new SsaTypeKnownRemover());
runPhase(new SsaTrustedCheckRemover(_options));
runPhase(new SsaAssignmentChaining(_closedWorld));
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 5bf162b..d643936 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -5,6 +5,7 @@
import '../constants/values.dart';
import '../elements/entities.dart';
import '../inferrer/abstract_value_domain.dart';
+import '../js_backend/interceptor_data.dart';
import '../options.dart';
import '../universe/selector.dart' show Selector;
import '../world.dart' show JClosedWorld;
@@ -26,14 +27,21 @@
///
/// - Remove NullChecks where the next instruction would fail on the operand.
///
+/// - Dummy receiver optimization.
+///
+/// - One-shot interceptor optimization.
+///
/// - Combine read/modify/write sequences into HReadModifyWrite instructions to
/// simplify codegen of expressions like `a.x += y`.
+
class SsaInstructionSelection extends HBaseVisitor with CodegenPhase {
final JClosedWorld _closedWorld;
+ final InterceptorData _interceptorData;
final CompilerOptions _options;
HGraph graph;
- SsaInstructionSelection(this._options, this._closedWorld);
+ SsaInstructionSelection(
+ this._options, this._closedWorld, this._interceptorData);
AbstractValueDomain get _abstractValueDomain =>
_closedWorld.abstractValueDomain;
@@ -221,6 +229,128 @@
.isPotentiallyTrue;
@override
+ HInstruction visitInvokeDynamic(HInvokeDynamic node) {
+ if (!node.isInterceptedCall) return node;
+
+ tryReplaceExplicitReceiverWithDummy(
+ node, node.selector, node.element, node.receiverType);
+
+ // Try to replace
+ //
+ // getInterceptor(o).method(o, ...)
+ //
+ // with a 'one shot interceptor' which is a call to a synthesized static
+ // helper function that combines the two operations.
+ //
+ // oneShotMethod(o, 1, 2)
+ //
+ // This saves code size and makes the receiver of an intercepted call a
+ // candidate for being generated at use site.
+ //
+ // Avoid combining a hoisted interceptor back into a loop, and the faster
+ // almost-constant kind of interceptor.
+
+ HInstruction interceptor = node.inputs[0];
+ if (interceptor is HInterceptor &&
+ interceptor.usedBy.length == 1 &&
+ !interceptor.isConditionalConstantInterceptor &&
+ interceptor.hasSameLoopHeaderAs(node)) {
+ // Copy inputs and replace interceptor with `null`.
+ List<HInstruction> inputs = List.of(node.inputs);
+ inputs[0] = graph.addConstantNull(_closedWorld);
+
+ HOneShotInterceptor oneShot = HOneShotInterceptor(
+ node.selector,
+ node.receiverType,
+ inputs,
+ node.instructionType,
+ node.typeArguments,
+ interceptor.interceptedClasses);
+ oneShot.sourceInformation = node.sourceInformation;
+ oneShot.sourceElement = node.sourceElement;
+ oneShot.sideEffects.setTo(node.sideEffects);
+
+ HBasicBlock block = node.block;
+ block.addAfter(node, oneShot);
+ block.rewrite(node, oneShot);
+ block.remove(node);
+ interceptor.block.remove(interceptor);
+ return null;
+ }
+
+ return node;
+ }
+
+ @override
+ HInstruction visitInvokeSuper(HInvokeSuper node) {
+ tryReplaceExplicitReceiverWithDummy(
+ node, node.selector, node.element, null);
+ return node;
+ }
+
+ @override
+ HInstruction visitOneShotInterceptor(HOneShotInterceptor node) {
+ throw StateError('Should not see HOneShotInterceptor: $node');
+ }
+
+ void tryReplaceExplicitReceiverWithDummy(HInvoke node, Selector selector,
+ MemberEntity target, AbstractValue mask) {
+ // Calls of the form
+ //
+ // a.foo$1(a, x)
+ //
+ // where the interceptor calling convention is used come from recognizing
+ // that 'a' is a 'self-interceptor'. If the selector matches only methods
+ // that ignore the explicit receiver parameter, replace occurrences of the
+ // receiver argument with a dummy receiver '0':
+ //
+ // a.foo$1(a, x) ---> a.foo$1(0, x)
+ //
+ // This often reduces the number of references to 'a' to one, allowing 'a'
+ // to be generated at use to avoid a temporary, e.g.
+ //
+ // t1 = b.get$thing();
+ // t1.foo$1(t1, x)
+ // --->
+ // b.get$thing().foo$1(0, x)
+ //
+ assert(target != null || mask != null);
+
+ if (!node.isInterceptedCall) return;
+
+ // TODO(15933): Make automatically generated property extraction closures
+ // work with the dummy receiver optimization.
+ if (selector.isGetter) return;
+
+ // This assignment of inputs is uniform for HInvokeDynamic and HInvokeSuper.
+ HInstruction interceptor = node.inputs[0];
+ HInstruction receiverArgument = node.inputs[1];
+
+ // A 'self-interceptor'?
+ if (interceptor.nonCheck() != receiverArgument.nonCheck()) return;
+
+ // TODO(sra): Should this be an assert?
+ if (!_interceptorData.isInterceptedSelector(selector)) return;
+
+ if (target != null) {
+ // A call that resolves to a single instance method (element) requires the
+ // calling convention consistent with the method.
+ ClassEntity cls = target.enclosingClass;
+ assert(_interceptorData.isInterceptedMethod(target));
+ if (_interceptorData.isInterceptedClass(cls)) return;
+ } else if (_interceptorData.isInterceptedMixinSelector(
+ selector, mask, _closedWorld)) {
+ return;
+ }
+
+ ConstantValue constant = DummyInterceptorConstantValue();
+ HConstant dummy = graph.addConstant(constant, _closedWorld);
+ receiverArgument.usedBy.remove(node);
+ node.inputs[1] = dummy;
+ dummy.usedBy.add(node);
+ }
+
+ @override
HInstruction visitFieldSet(HFieldSet setter) {
// Pattern match
// t1 = x.f; t2 = t1 + 1; x.f = t2; use(t2) --> ++x.f
diff --git a/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart b/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart
deleted file mode 100644
index 9ec829d..0000000
--- a/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart
+++ /dev/null
@@ -1,240 +0,0 @@
-// Copyright (c) 2021, 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.
-
-import '../constants/values.dart';
-import '../elements/entities.dart';
-import '../inferrer/abstract_value_domain.dart';
-import '../js_backend/interceptor_data.dart';
-import '../universe/selector.dart' show Selector;
-import '../world.dart' show JClosedWorld;
-import 'nodes.dart';
-import 'optimize.dart';
-
-/// SsaFinalizeInterceptors makes adjustments for the interceptor calling
-/// convention.
-///
-/// 1. If the method cannot be invoked with an intercepted receiver, the
-/// receiver and interceptor are the same. In this case ignore the explicit
-/// receiver argument and use the interceptor (this) as the receiver.
-///
-/// 2. The call-site dual of the above is if a method ignores the explicit
-/// receiver, it can be replaced with a dummy value, i.e. a dummy receiver
-/// optimization.
-///
-/// 3. If an interceptor is used once for a call, replace the
-/// getInterceptor-call pair with a call to a 'one-shot interceptor' outlined
-/// method.
-///
-class SsaFinalizeInterceptors extends HBaseVisitor
- implements OptimizationPhase {
- @override
- String get name => "SsaFinalizeInterceptors";
- final JClosedWorld _closedWorld;
- HGraph _graph;
-
- SsaFinalizeInterceptors(this._closedWorld);
-
- InterceptorData get _interceptorData => _closedWorld.interceptorData;
-
- @override
- void visitGraph(HGraph graph) {
- _graph = graph;
- MemberEntity element = graph.element;
-
- if (usesSelfInterceptor(element)) {
- _redirectReceiver();
- }
- visitDominatorTree(graph);
- }
-
- @override
- visitBasicBlock(HBasicBlock node) {
- HInstruction instruction = node.first;
- while (instruction != null) {
- final next = instruction.next;
- instruction.accept(this);
- instruction = next;
- }
- }
-
- /// Returns `true` if [element] is an instance method that uses the
- /// interceptor calling convention but the instance and interceptor arguments
- /// will always be the same value.
- bool usesSelfInterceptor(MemberEntity element) {
- if (!_interceptorData.isInterceptedMethod(element)) return false;
- ClassEntity cls = element.enclosingClass;
- return !_interceptorData.isInterceptedClass(cls);
- }
-
- void _redirectReceiver() {
- // The entry block contains the parameters in order, starting with `this`,
- // and then the explicit receiver. There are other instructions in the
- // block, like constants, which we ignore.
- HThis thisParameter;
- HParameterValue receiverParameter;
- for (HInstruction node = _graph.entry.first;
- node != null;
- node = node.next) {
- if (node is HParameterValue) {
- if (node is HThis) {
- thisParameter = node;
- } else {
- receiverParameter = node;
- break;
- }
- }
- }
- assert(thisParameter != null,
- '`this` parameter should be before other parameters');
- assert(receiverParameter != null,
- 'Intercepted convention requires explicit receiver');
- thisParameter.instructionType = receiverParameter.instructionType;
- receiverParameter.block.rewrite(receiverParameter, thisParameter);
- receiverParameter.sourceElement = const _RenameToUnderscore();
- }
-
- @override
- void visitInvokeDynamic(HInvokeDynamic node) {
- if (!node.isInterceptedCall) return;
-
- if (node.element != null) {
- tryReplaceExplicitReceiverForTargetWithDummy(
- node, node.selector, node.element);
- } else {
- tryReplaceExplicitReceiverForSelectorWithDummy(
- node, node.selector, node.receiverType);
- }
-
- // Try to replace
- //
- // getInterceptor(o).method(o, ...)
- //
- // with a 'one shot interceptor' which is a call to a synthesized static
- // helper function that combines the two operations.
- //
- // oneShotMethod(o, 1, 2)
- //
- // This saves code size and makes the receiver of an intercepted call a
- // candidate for being generated at use site.
- //
- // Avoid combining a hoisted interceptor back into a loop, and the faster
- // almost-constant kind of interceptor.
-
- HInstruction interceptor = node.inputs[0];
- if (interceptor is HInterceptor &&
- interceptor.usedBy.length == 1 &&
- !interceptor.isConditionalConstantInterceptor &&
- interceptor.hasSameLoopHeaderAs(node)) {
- // Copy inputs and replace interceptor with `null`.
- List<HInstruction> inputs = List.of(node.inputs);
- inputs[0] = _graph.addConstantNull(_closedWorld);
-
- HOneShotInterceptor oneShot = HOneShotInterceptor(
- node.selector,
- node.receiverType,
- inputs,
- node.instructionType,
- node.typeArguments,
- interceptor.interceptedClasses);
- oneShot.sourceInformation = node.sourceInformation;
- oneShot.sourceElement = node.sourceElement;
- oneShot.sideEffects.setTo(node.sideEffects);
-
- HBasicBlock block = node.block;
- block.addAfter(node, oneShot);
- block.rewrite(node, oneShot);
- block.remove(node);
- interceptor.block.remove(interceptor);
- }
- }
-
- @override
- void visitInvokeSuper(HInvokeSuper node) {
- if (!node.isInterceptedCall) return;
- tryReplaceExplicitReceiverForTargetWithDummy(
- node, node.selector, node.element);
- }
-
- @override
- void visitInvokeGeneratorBody(HInvokeGeneratorBody node) {
- tryReplaceExplicitReceiverForTargetWithDummy(node, null, node.element);
- }
-
- @override
- void visitOneShotInterceptor(HOneShotInterceptor node) {
- throw StateError('Should not see HOneShotInterceptor: $node');
- }
-
- void tryReplaceExplicitReceiverForTargetWithDummy(
- HInvoke node, Selector selector, MemberEntity target) {
- assert(target != null);
-
- // TODO(15933): Make automatically generated property extraction closures
- // work with the dummy receiver optimization.
- if (selector != null && selector.isGetter) return;
-
- if (usesSelfInterceptor(target)) {
- _replaceReceiverArgumentWithDummy(node, 1);
- }
- }
-
- void tryReplaceExplicitReceiverForSelectorWithDummy(
- HInvoke node, Selector selector, AbstractValue mask) {
- assert(mask != null);
- // Calls of the form
- //
- // a.foo$1(a, x)
- //
- // where the interceptor calling convention is used come from recognizing
- // that 'a' is a 'self-interceptor'. If the selector matches only methods
- // that ignore the explicit receiver parameter, replace occurrences of the
- // receiver argument with a dummy receiver '0':
- //
- // a.foo$1(a, x) ---> a.foo$1(0, x)
- //
- // This often reduces the number of references to 'a' to one, allowing 'a'
- // to be generated at use to avoid a temporary, e.g.
- //
- // t1 = b.get$thing();
- // t1.foo$1(t1, x)
- // --->
- // b.get$thing().foo$1(0, x)
- //
-
- // TODO(15933): Make automatically generated property extraction closures
- // work with the dummy receiver optimization.
- if (selector.isGetter) return;
-
- // This assignment of inputs is uniform for HInvokeDynamic and HInvokeSuper.
- HInstruction interceptor = node.inputs[0];
- HInstruction receiverArgument = node.inputs[1];
-
- // A 'self-interceptor'?
- if (interceptor.nonCheck() != receiverArgument.nonCheck()) return;
-
- // TODO(sra): Should this be an assert?
- if (!_interceptorData.isInterceptedSelector(selector)) return;
-
- if (!_interceptorData.isInterceptedMixinSelector(
- selector, mask, _closedWorld)) {
- _replaceReceiverArgumentWithDummy(node, 1);
- }
- }
-
- void _replaceReceiverArgumentWithDummy(HInvoke node, int receiverIndex) {
- HInstruction receiverArgument = node.inputs[receiverIndex];
- ConstantValue constant = DummyInterceptorConstantValue();
- HConstant dummy = _graph.addConstant(constant, _closedWorld);
- receiverArgument.usedBy.remove(node);
- node.inputs[receiverIndex] = dummy;
- dummy.usedBy.add(node);
- }
-}
-
-/// A simple Entity to rename the unused receiver to `_` in non-minified code.
-class _RenameToUnderscore implements Entity {
- const _RenameToUnderscore();
- @override
- String get name => '_';
-}
diff --git a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
index c0b6254..968cf4f 100644
--- a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
+++ b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
@@ -12,16 +12,20 @@
import 'nodes.dart';
import 'optimize.dart';
-/// This phase computes the set of classes dispatched by an interceptor, and
-/// simplifies interceptors in multiple ways:
+/// This phase simplifies interceptors in multiple ways:
///
/// 1) If the interceptor is for an object whose type is known, it
-/// tries to use a constant interceptor instead.
+/// tries to use a constant interceptor instead.
///
/// 2) Interceptors are specialized based on the selector it is used with.
///
/// 3) If we know the object is not intercepted, we just use the object
-/// instead.
+/// instead.
+///
+/// 4) Single use interceptors at dynamic invoke sites are replaced with 'one
+/// shot interceptors' which are synthesized static helper functions that fetch
+/// the interceptor and then call the method. This saves code size and makes the
+/// receiver of an intercepted call a candidate for being generated at use site.
///
class SsaSimplifyInterceptors extends HBaseVisitor
implements OptimizationPhase {
diff --git a/pkg/compiler/lib/src/ssa/locals_handler.dart b/pkg/compiler/lib/src/ssa/locals_handler.dart
index 502d335..ea01915 100644
--- a/pkg/compiler/lib/src/ssa/locals_handler.dart
+++ b/pkg/compiler/lib/src/ssa/locals_handler.dart
@@ -288,7 +288,9 @@
element.isGenerativeConstructor &&
_nativeData.isNativeOrExtendsNative(cls);
if (_interceptorData.isInterceptedMethod(element)) {
- SyntheticLocal parameter = createLocal('receiver');
+ bool isInterceptedClass = _interceptorData.isInterceptedClass(cls);
+ String name = isInterceptedClass ? 'receiver' : '_';
+ SyntheticLocal parameter = createLocal(name);
HParameterValue value = new HParameterValue(parameter, getTypeOfThis());
builder.graph.explicitReceiverParameter = value;
builder.graph.entry.addAfter(directLocals[scopeInfo.thisLocal], value);
@@ -296,7 +298,10 @@
// If this is the first parameter inserted, make sure it stays first.
builder.lastAddedParameter = value;
}
- directLocals[scopeInfo.thisLocal] = value;
+ if (isInterceptedClass) {
+ // Only use the extra parameter in intercepted classes.
+ directLocals[scopeInfo.thisLocal] = value;
+ }
} else if (isNativeUpgradeFactory) {
SyntheticLocal parameter = createLocal('receiver');
// Unlike `this`, receiver is nullable since direct calls to generative
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 8a819a1..e0f7369 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -34,7 +34,6 @@
import '../util/util.dart';
import '../world.dart' show JClosedWorld;
import 'interceptor_simplifier.dart';
-import 'interceptor_finalizer.dart';
import 'logging.dart';
import 'nodes.dart';
import 'types.dart';
@@ -130,9 +129,9 @@
];
phases.forEach(runPhase);
- // Simplifying interceptors is just an optimization, it is required for
- // implementation correctness because the code generator assumes it is
- // always performed to compute the intercepted classes sets.
+ // Simplifying interceptors is not strictly just an optimization, it is
+ // required for implementation correctness because the code generator
+ // assumes it is always performed.
runPhase(new SsaSimplifyInterceptors(closedWorld, member.enclosingClass));
SsaDeadCodeEliminator dce = new SsaDeadCodeEliminator(closedWorld, this);
@@ -164,13 +163,6 @@
}
phases.forEach(runPhase);
});
-
- // SsaFinalizeInterceptors must always be run to ensure consistent calling
- // conventions between SSA-generated code and other code fragments generated
- // by the emitter.
- // TODO(sra): Generate these other fragments via SSA, then this phase
- // becomes an opt-in optimization.
- runPhase(SsaFinalizeInterceptors(closedWorld));
}
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 89fdd5e..3458ef6 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -1480,7 +1480,6 @@
replacement = inferrer.helper.buildProblem(
messageNullableSpreadError, receiver.fileOffset, 1,
context: inferrer.getWhyNotPromotedContext(
- receiver,
inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
element,
(type) => !type.isPotentiallyNullable));
@@ -1550,7 +1549,6 @@
replacement = inferrer.helper.buildProblem(
messageNullableSpreadError, receiver.fileOffset, 1,
context: inferrer.getWhyNotPromotedContext(
- receiver,
inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
element,
(type) => !type.isPotentiallyNullable));
@@ -1988,7 +1986,6 @@
Expression problem = inferrer.helper.buildProblem(
messageNullableSpreadError, receiver.fileOffset, 1,
context: inferrer.getWhyNotPromotedContext(
- receiver,
inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
entry,
(type) => !type.isPotentiallyNullable));
@@ -2008,7 +2005,6 @@
receiver.fileOffset,
1,
context: inferrer.getWhyNotPromotedContext(
- receiver,
inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
entry,
(type) => !type.isPotentiallyNullable));
@@ -2119,7 +2115,6 @@
keyError = inferrer.helper.buildProblem(
messageNullableSpreadError, receiver.fileOffset, 1,
context: inferrer.getWhyNotPromotedContext(
- receiver,
inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
entry,
(type) => !type.isPotentiallyNullable));
@@ -2884,8 +2879,9 @@
}
ExpressionInferenceResult readResult = _computePropertyGet(node.readOffset,
- readReceiver, receiverType, node.propertyName, const UnknownType(),
- isThisReceiver: node.receiver is ThisExpression);
+ readReceiver, receiverType, node.propertyName, const UnknownType(),
+ isThisReceiver: node.receiver is ThisExpression)
+ .expressionInferenceResult;
Expression read = readResult.expression;
DartType readType = readResult.inferredType;
@@ -2941,8 +2937,9 @@
Expression writeReceiver = createVariableGet(receiverVariable);
ExpressionInferenceResult readResult = _computePropertyGet(node.readOffset,
- readReceiver, receiverType, node.propertyName, const UnknownType(),
- isThisReceiver: node.receiver is ThisExpression);
+ readReceiver, receiverType, node.propertyName, const UnknownType(),
+ isThisReceiver: node.receiver is ThisExpression)
+ .expressionInferenceResult;
reportNonNullableInNullAwareWarningIfNeeded(
readResult.inferredType, "??=", node.readOffset);
@@ -4695,7 +4692,7 @@
/// [typeContext] is used to create implicit generic tearoff instantiation
/// if necessary. [isThisReceiver] must be set to `true` if the receiver is a
/// `this` expression.
- ExpressionInferenceResult _computePropertyGet(
+ PropertyGetInferenceResult _computePropertyGet(
int fileOffset,
Expression receiver,
DartType receiverType,
@@ -4879,12 +4876,11 @@
read.fileOffset,
propertyName.text.length,
context: inferrer.getWhyNotPromotedContext(
- receiver,
inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
read,
(type) => !type.isPotentiallyNullable));
}
- return readResult;
+ return new PropertyGetInferenceResult(readResult, readTarget.member);
}
/// Creates a property set operation of [writeTarget] on [receiver] using
@@ -5216,12 +5212,13 @@
DartType nonNullReceiverType = receiverType.toNonNull();
ExpressionInferenceResult readResult = _computePropertyGet(
- node.readOffset,
- readReceiver,
- nonNullReceiverType,
- node.propertyName,
- const UnknownType(),
- isThisReceiver: node.receiver is ThisExpression);
+ node.readOffset,
+ readReceiver,
+ nonNullReceiverType,
+ node.propertyName,
+ const UnknownType(),
+ isThisReceiver: node.receiver is ThisExpression)
+ .expressionInferenceResult;
Expression read = readResult.expression;
DartType readType = readResult.inferredType;
@@ -5752,8 +5749,9 @@
DartType nonNullReceiverType = receiverType.toNonNull();
ExpressionInferenceResult readResult = _computePropertyGet(node.readOffset,
- readReceiver, nonNullReceiverType, node.name, typeContext,
- isThisReceiver: node.receiver is ThisExpression);
+ readReceiver, nonNullReceiverType, node.name, typeContext,
+ isThisReceiver: node.receiver is ThisExpression)
+ .expressionInferenceResult;
Expression read = readResult.expression;
DartType readType = readResult.inferredType;
inferrer.flowAnalysis.ifNullExpression_rightBegin(read, readType);
@@ -5845,11 +5843,13 @@
DartType receiverType = result.nullAwareActionType;
node.receiver = receiver..parent = node;
- ExpressionInferenceResult readResult = _computePropertyGet(
+ PropertyGetInferenceResult propertyGetInferenceResult = _computePropertyGet(
node.fileOffset, receiver, receiverType, node.name, typeContext,
isThisReceiver: node.receiver is ThisExpression);
- inferrer.flowAnalysis.propertyGet(
- node, node.receiver, node.name.text, readResult.inferredType);
+ ExpressionInferenceResult readResult =
+ propertyGetInferenceResult.expressionInferenceResult;
+ inferrer.flowAnalysis.propertyGet(node, node.receiver, node.name.text,
+ propertyGetInferenceResult.member, readResult.inferredType);
ExpressionInferenceResult expressionInferenceResult =
inferrer.createNullAwareExpressionInferenceResult(
readResult.inferredType, readResult.expression, nullAwareGuards);
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 15606b8..463c05b 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -290,14 +290,13 @@
/// promoted, to be used when reporting an error for a larger expression
/// containing [receiver]. [node] is the containing tree node.
List<LocatedMessage> getWhyNotPromotedContext(
- Expression receiver,
Map<DartType, NonPromotionReason> whyNotPromoted,
TreeNode node,
bool Function(DartType) typeFilter) {
List<LocatedMessage> context;
if (whyNotPromoted != null && whyNotPromoted.isNotEmpty) {
_WhyNotPromotedVisitor whyNotPromotedVisitor =
- new _WhyNotPromotedVisitor(this, receiver);
+ new _WhyNotPromotedVisitor(this);
for (core.MapEntry<DartType, NonPromotionReason> entry
in whyNotPromoted.entries) {
if (!typeFilter(entry.key)) continue;
@@ -615,7 +614,6 @@
nullabilityErrorTemplate.withArguments(expressionType,
declaredContextType ?? contextType, isNonNullableByDefault),
context: getWhyNotPromotedContext(
- expression,
flowAnalysis?.whyNotPromoted(expression)(),
expression,
(type) => typeSchemaEnvironment.isSubtypeOf(type,
@@ -2826,7 +2824,6 @@
// void Function() get call => () {};
// }
List<LocatedMessage> context = getWhyNotPromotedContext(
- receiver,
flowAnalysis?.whyNotPromoted(receiver)(),
staticInvocation,
(type) => !type.isPotentiallyNullable);
@@ -2858,7 +2855,6 @@
Expression replacement = result.applyResult(staticInvocation);
if (!isTopLevel && target.isNullable) {
List<LocatedMessage> context = getWhyNotPromotedContext(
- receiver,
flowAnalysis?.whyNotPromoted(receiver)(),
staticInvocation,
(type) => !type.isPotentiallyNullable);
@@ -2972,7 +2968,6 @@
Expression replacement = result.applyResult(expression);
if (!isTopLevel && target.isNullableCallFunction) {
List<LocatedMessage> context = getWhyNotPromotedContext(
- receiver,
flowAnalysis?.whyNotPromoted(receiver)(),
expression,
(type) => !type.isPotentiallyNullable);
@@ -3147,7 +3142,6 @@
replacement = result.applyResult(replacement);
if (!isTopLevel && target.isNullable) {
List<LocatedMessage> context = getWhyNotPromotedContext(
- receiver,
flowAnalysis?.whyNotPromoted(receiver)(),
expression,
(type) => !type.isPotentiallyNullable);
@@ -3308,7 +3302,6 @@
// void Function() get foo => () {};
// }
List<LocatedMessage> context = getWhyNotPromotedContext(
- receiver,
flowAnalysis?.whyNotPromoted(receiver)(),
invocationResult.expression,
(type) => !type.isPotentiallyNullable);
@@ -3465,8 +3458,8 @@
new PropertyGet(originalReceiver, originalName, originalTarget)
..fileOffset = fileOffset;
}
- flowAnalysis.propertyGet(
- originalPropertyGet, originalReceiver, originalName.text, calleeType);
+ flowAnalysis.propertyGet(originalPropertyGet, originalReceiver,
+ originalName.text, originalTarget, calleeType);
Expression propertyGet = originalPropertyGet;
if (receiver is! ThisExpression &&
calleeType is! DynamicType &&
@@ -3518,11 +3511,8 @@
// }
// TODO(paulberry): would it be better to report NullableMethodCallError
// in this scenario?
- List<LocatedMessage> context = getWhyNotPromotedContext(
- receiver,
- whyNotPromoted(),
- invocationResult.expression,
- (type) => !type.isPotentiallyNullable);
+ List<LocatedMessage> context = getWhyNotPromotedContext(whyNotPromoted(),
+ invocationResult.expression, (type) => !type.isPotentiallyNullable);
invocationResult = wrapExpressionInferenceResultInProblem(
invocationResult,
templateNullableExpressionCallError.withArguments(
@@ -3853,7 +3843,7 @@
return instantiateTearOff(inferredType, typeContext, expression);
}
flowAnalysis.thisOrSuperPropertyGet(
- expression, expression.name.name, inferredType);
+ expression, expression.name.name, member, inferredType);
return new ExpressionInferenceResult(inferredType, expression);
}
@@ -4644,6 +4634,17 @@
}
}
+/// The result of inference of a property get expression.
+class PropertyGetInferenceResult {
+ /// The main inference result.
+ final ExpressionInferenceResult expressionInferenceResult;
+
+ /// The property that was looked up, or `null` if no property was found.
+ final Member member;
+
+ PropertyGetInferenceResult(this.expressionInferenceResult, this.member);
+}
+
/// The result of an expression inference.
class ExpressionInferenceResult {
/// The inferred type of the expression.
@@ -5134,13 +5135,11 @@
DartType> {
final TypeInferrerImpl inferrer;
- final Expression receiver;
-
Member propertyReference;
DartType propertyType;
- _WhyNotPromotedVisitor(this.inferrer, this.receiver);
+ _WhyNotPromotedVisitor(this.inferrer);
@override
LocatedMessage visitDemoteViaExplicitWrite(
@@ -5158,26 +5157,16 @@
@override
LocatedMessage visitPropertyNotPromoted(PropertyNotPromoted reason) {
- Member member;
- Expression receiver = this.receiver;
- if (receiver is InstanceGet) {
- member = receiver.interfaceTarget;
- } else if (receiver is SuperPropertyGet) {
- member = receiver.interfaceTarget;
- } else if (receiver is StaticInvocation) {
- member = receiver.target;
- } else if (receiver is PropertyGet) {
- member = receiver.interfaceTarget;
- } else {
- assert(false, 'Unrecognized receiver: ${receiver.runtimeType}');
- }
- if (member != null) {
+ Object member = reason.propertyMember;
+ if (member is Member) {
propertyReference = member;
propertyType = reason.staticType;
return templateFieldNotPromoted
.withArguments(reason.propertyName, reason.documentationLink)
.withLocation(member.fileUri, member.fileOffset, noLength);
} else {
+ assert(member == null,
+ 'Unrecognized property member: ${member.runtimeType}');
return null;
}
}
diff --git a/tools/VERSION b/tools/VERSION
index 207f847..9bcc89c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 207
+PRERELEASE 208
PRERELEASE_PATCH 0
\ No newline at end of file