Index: pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart |
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart |
index 349ccbf41d02bf586c2b4a6f9fc809335c078a14..a44596ec796431723716af5a87fa6b9772d875fb 100644 |
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart |
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart |
@@ -29,6 +29,7 @@ import 'package:kernel/ast.dart' |
hide InvalidExpression, InvalidInitializer, InvalidStatement; |
import 'package:kernel/frontend/accessors.dart'; |
import 'package:kernel/type_algebra.dart'; |
+import 'package:kernel/type_environment.dart'; |
import '../errors.dart' show internalError; |
@@ -234,12 +235,85 @@ class KernelCascadeExpression extends Let implements KernelExpression { |
/// Concrete shadow object representing a complex assignment in kernel form. |
/// |
+/// Since there are many forms a complex assignment might have been desugared |
+/// to, this class wraps the desugared assignment rather than extending it. |
+/// |
/// TODO(paulberry): once we know exactly what constitutes a "complex |
/// assignment", document it here. It will probably be something like: an |
/// assignment that desugars to a "let" expression. |
-class KernelComplexAssign extends Let implements KernelExpression { |
- KernelComplexAssign(VariableDeclaration variable, Expression body) |
- : super(variable, body); |
+class KernelComplexAssignment extends Expression implements KernelExpression { |
+ /// The full desugared assignment expression |
+ Expression desugared; |
+ |
+ /// The receiver of the assignment target (e.g. `a` in `a[b] = c`), or `null` |
+ /// if there is no receiver. |
+ Expression receiver; |
+ |
+ /// In an assignment to an index expression, the index expression, or `null` |
+ /// if this is not an assignment to an index expression. |
+ Expression index; |
+ |
+ /// In a compound assignment, the expression that reads the old value, or |
+ /// `null` if this is not a compound assignment. |
+ Expression read; |
+ |
+ /// The expression appearing on the RHS of the assignment. |
+ Expression rhs; |
+ |
+ /// The expression that performs the write (e.g. `a.[]=(b, a.[](b) + 1)` in |
+ /// `++a[b]`). |
+ Expression write; |
+ |
+ /// In a compound assignment without shortcut semantics, the expression that |
+ /// combines the old and new values, or `null` if this is not a compound |
+ /// assignment. |
+ /// |
+ /// Note that in a compound assignment with shortcut semantics, this is not |
+ /// used; [nullAwareCombiner] is used instead. |
+ MethodInvocation combiner; |
+ |
+ /// In a compound assignment with shortcut semantics, the conditional |
+ /// expression that determines whether the assignment occurs. |
+ /// |
+ /// Note that in a compound assignment without shortcut semantics, this is not |
+ /// used; [combiner] is used instead. |
+ ConditionalExpression nullAwareCombiner; |
+ |
+ /// Indicates whether the expression arose from a post-increment or |
+ /// post-decrement. |
+ bool isPostIncDec = false; |
+ |
+ @override |
+ accept(ExpressionVisitor v) => desugared.accept(v); |
+ |
+ @override |
+ accept1(ExpressionVisitor1 v, arg) => desugared.accept1(v, arg); |
+ |
+ @override |
+ DartType getStaticType(TypeEnvironment types) => |
+ desugared.getStaticType(types); |
+ |
+ String toString() { |
+ List<String> parts = []; |
+ if (desugared != null) parts.add('desugared=$desugared'); |
+ if (receiver != null) parts.add('receiver=$receiver'); |
+ if (index != null) parts.add('index=$index'); |
+ if (read != null) parts.add('read=$read'); |
+ if (rhs != null) parts.add('rhs=$rhs'); |
+ if (write != null) parts.add('write=$write'); |
+ if (combiner != null) parts.add('combiner=$combiner'); |
+ if (nullAwareCombiner != null) { |
+ parts.add('nullAwareCombiner=$nullAwareCombiner'); |
+ } |
+ if (isPostIncDec) parts.add('isPostIncDec=true'); |
+ return 'KernelComplexAssignment(${parts.join(', ')})'; |
+ } |
+ |
+ @override |
+ transformChildren(Transformer v) => desugared.transformChildren(v); |
+ |
+ @override |
+ visitChildren(Visitor v) => desugared.visitChildren(v); |
@override |
void _collectDependencies(KernelDependencyCollector collector) { |
@@ -250,7 +324,58 @@ class KernelComplexAssign extends Let implements KernelExpression { |
@override |
DartType _inferExpression( |
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
- return inferrer.inferIndexAssign(this, typeContext, typeNeeded); |
+ typeNeeded = inferrer.listener.indexAssignEnter(desugared, typeContext) || |
+ typeNeeded; |
+ // TODO(paulberry): record the appropriate types on let variables and |
+ // conditional expressions. |
+ var receiverType = inferrer.inferExpression(receiver, null, true); |
+ if (read != null) { |
+ inferrer.findMethodInvocationMember(receiverType, read, silent: true); |
+ } |
+ var writeMember = inferrer.findMethodInvocationMember(receiverType, write); |
+ // To replicate analyzer behavior, we base type inference on the write |
+ // member. TODO(paulberry): would it be better to use the read member |
+ // when doing compound assignment? |
+ var calleeType = |
+ inferrer.getCalleeType(writeMember, receiverType, indexSetName); |
+ DartType indexContext; |
+ DartType rhsContext; |
+ DartType inferredType; |
+ if (calleeType is FunctionType && |
+ calleeType.positionalParameters.length >= 2) { |
+ // TODO(paulberry): we ought to get a context for the index expression |
+ // from the index formal parameter, but analyzer doesn't so for now we |
+ // replicate its behavior. |
+ indexContext = null; |
+ rhsContext = calleeType.positionalParameters[1]; |
+ if (combiner != null) { |
+ var combinerMember = inferrer |
+ .findMethodInvocationMember(rhsContext, combiner, silent: true); |
+ if (isPostIncDec) { |
+ inferredType = rhsContext; |
+ } else { |
+ inferredType = inferrer |
+ .getCalleeFunctionType( |
+ combinerMember, rhsContext, combiner.name, false) |
+ .returnType; |
+ } |
+ // Analyzer uses a null context for the RHS here. |
+ // TODO(paulberry): improve on this. |
+ rhsContext = null; |
+ } else { |
+ inferredType = rhsContext; |
+ } |
+ } else { |
+ inferredType = const DynamicType(); |
+ } |
+ inferrer.inferExpression(index, indexContext, false); |
+ inferrer.inferExpression(rhs, rhsContext, false); |
+ if (nullAwareCombiner != null) { |
+ MethodInvocation equalsInvocation = nullAwareCombiner.condition; |
+ inferrer.findMethodInvocationMember(rhsContext, equalsInvocation); |
+ } |
+ inferrer.listener.indexAssignExit(desugared, inferredType); |
+ return inferredType; |
} |
} |
@@ -258,15 +383,9 @@ class KernelComplexAssign extends Let implements KernelExpression { |
/// Shadow object for [ConditionalExpression]. |
class KernelConditionalExpression extends ConditionalExpression |
implements KernelExpression { |
- /// Indicates whether this conditional expression is associated with a `??=` |
- /// in a null-aware compound assignment. |
- final bool _isNullAwareCombiner; |
- |
KernelConditionalExpression( |
- Expression condition, Expression then, Expression otherwise, |
- {bool isNullAwareCombiner: false}) |
- : _isNullAwareCombiner = isNullAwareCombiner, |
- super(condition, then, otherwise, const DynamicType()); |
+ Expression condition, Expression then, Expression otherwise) |
+ : super(condition, then, otherwise, const DynamicType()); |
@override |
void _collectDependencies(KernelDependencyCollector collector) { |
@@ -296,11 +415,6 @@ class KernelConditionalExpression extends ConditionalExpression |
inferrer.listener.conditionalExpressionExit(this, inferredType); |
return inferredType; |
} |
- |
- /// Helper method allowing [_isNullAwareCombiner] to be checked from outside |
- /// this library without adding a public member to the class. |
- static bool isNullAwareCombiner(KernelConditionalExpression e) => |
- e._isNullAwareCombiner; |
} |
/// Shadow object for [ConstructorInvocation]. |
@@ -1071,16 +1185,9 @@ class KernelMethodInvocation extends MethodInvocation |
/// resulting from the invocation of a function expression. |
final bool _isImplicitCall; |
- /// Indicates whether this method invocation invokes the operator that |
- /// combines old and new values in a compound assignment. |
- final bool _isCombiner; |
- |
KernelMethodInvocation(Expression receiver, Name name, Arguments arguments, |
- {bool isImplicitCall: false, |
- Member interfaceTarget, |
- bool isCombiner: false}) |
+ {bool isImplicitCall: false, Member interfaceTarget}) |
: _isImplicitCall = isImplicitCall, |
- _isCombiner = isCombiner, |
super(receiver, name, arguments, interfaceTarget); |
@override |
@@ -1093,9 +1200,6 @@ class KernelMethodInvocation extends MethodInvocation |
@override |
DartType _inferExpression( |
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
- if (identical(name.name, '[]=')) { |
- return inferrer.inferIndexAssign(this, typeContext, typeNeeded); |
- } |
typeNeeded = inferrer.listener.methodInvocationEnter(this, typeContext) || |
typeNeeded; |
// First infer the receiver so we can look up the method that was invoked. |
@@ -1116,10 +1220,6 @@ class KernelMethodInvocation extends MethodInvocation |
inferrer.listener.methodInvocationExit(this, inferredType); |
return inferredType; |
} |
- |
- /// Helper method allowing [_isCombiner] to be checked from outside this |
- /// library without adding a public member to the class. |
- static bool isCombiner(KernelMethodInvocation e) => e._isCombiner; |
} |
/// Shadow object for [Not]. |
@@ -1806,10 +1906,6 @@ class KernelVariableDeclaration extends VariableDeclaration |
final bool _isLocalFunction; |
- /// Indicates whether this variable declaration exists for the sole purpose of |
- /// discarding a return value in a complex desugared expression. |
- final bool _isDiscarding; |
- |
KernelVariableDeclaration(String name, this._functionNestingLevel, |
{Expression initializer, |
DartType type, |
@@ -1818,7 +1914,6 @@ class KernelVariableDeclaration extends VariableDeclaration |
bool isLocalFunction: false}) |
: _implicitlyTyped = type == null, |
_isLocalFunction = isLocalFunction, |
- _isDiscarding = false, |
super(name, |
initializer: initializer, |
type: type ?? const DynamicType(), |
@@ -1826,11 +1921,9 @@ class KernelVariableDeclaration extends VariableDeclaration |
isConst: isConst); |
KernelVariableDeclaration.forValue( |
- Expression initializer, this._functionNestingLevel, |
- {bool isDiscarding: false}) |
+ Expression initializer, this._functionNestingLevel) |
: _implicitlyTyped = true, |
_isLocalFunction = false, |
- _isDiscarding = isDiscarding, |
super.forValue(initializer); |
@override |
@@ -1849,11 +1942,6 @@ class KernelVariableDeclaration extends VariableDeclaration |
inferrer.listener.variableDeclarationExit(this); |
} |
- /// Helper method allowing [_isDiscarding] to be checked from outside this |
- /// library without adding a public member to the class. |
- static bool isDiscarding(VariableDeclaration v) => |
- v is KernelVariableDeclaration && v._isDiscarding; |
- |
/// Determine whether the given [KernelVariableDeclaration] had an implicit |
/// type. |
/// |