| 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 a44596ec796431723716af5a87fa6b9772d875fb..67eaee84965d0afdd4aafb97d012d3e93e9d3ff0 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
|
| @@ -233,32 +233,24 @@ class KernelCascadeExpression extends Let implements KernelExpression {
|
| }
|
| }
|
|
|
| -/// Concrete shadow object representing a complex assignment in kernel form.
|
| +/// Abstract 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 KernelComplexAssignment extends Expression implements KernelExpression {
|
| +/// assignment", document it here.
|
| +abstract 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;
|
| + final Expression rhs;
|
|
|
| /// The expression that performs the write (e.g. `a.[]=(b, a.[](b) + 1)` in
|
| /// `++a[b]`).
|
| @@ -283,6 +275,12 @@ class KernelComplexAssignment extends Expression implements KernelExpression {
|
| /// post-decrement.
|
| bool isPostIncDec = false;
|
|
|
| + /// Indicates whether the expression arose from a pre-increment or
|
| + /// pre-decrement.
|
| + bool isPreIncDec = false;
|
| +
|
| + KernelComplexAssignment(this.rhs);
|
| +
|
| @override
|
| accept(ExpressionVisitor v) => desugared.accept(v);
|
|
|
| @@ -294,19 +292,8 @@ class KernelComplexAssignment extends Expression implements KernelExpression {
|
| 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(', ')})';
|
| + var parts = _getToStringParts();
|
| + return '${runtimeType}(${parts.join(', ')})';
|
| }
|
|
|
| @override
|
| @@ -321,61 +308,61 @@ class KernelComplexAssignment extends Expression implements KernelExpression {
|
| collector.recordNotImmediatelyEvident(fileOffset);
|
| }
|
|
|
| - @override
|
| - DartType _inferExpression(
|
| - KernelTypeInferrer inferrer, DartType typeContext, bool 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);
|
| + List<String> _getToStringParts() {
|
| + List<String> parts = [];
|
| + if (desugared != null) parts.add('desugared=$desugared');
|
| + 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');
|
| }
|
| - 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;
|
| + if (isPostIncDec) parts.add('isPostIncDec=true');
|
| + if (isPreIncDec) parts.add('isPreIncDec=true');
|
| + return parts;
|
| + }
|
| +
|
| + DartType _inferRhs(KernelTypeInferrer inferrer, DartType writeContext) {
|
| + DartType inferredType = writeContext ?? const DynamicType();
|
| + if (nullAwareCombiner != null) {
|
| + var rhsType = inferrer.inferExpression(rhs, writeContext, true);
|
| + MethodInvocation equalsInvocation = nullAwareCombiner.condition;
|
| + inferrer.findMethodInvocationMember(writeContext, equalsInvocation);
|
| + return inferrer.typeSchemaEnvironment
|
| + .getLeastUpperBound(inferredType, rhsType);
|
| + } else if (combiner != null) {
|
| + bool isOverloadedArithmeticOperator = false;
|
| + var combinerMember = inferrer
|
| + .findMethodInvocationMember(writeContext, combiner, silent: true);
|
| + if (combinerMember is Procedure) {
|
| + isOverloadedArithmeticOperator = inferrer.typeSchemaEnvironment
|
| + .isOverloadedArithmeticOperator(combinerMember);
|
| + }
|
| + if (isPostIncDec) {
|
| + return inferredType;
|
| + } else {
|
| + DartType rhsType;
|
| + if (isPreIncDec) {
|
| + rhsType = inferrer.coreTypes.intClass.rawType;
|
| + } else {
|
| + // Analyzer uses a null context for the RHS here.
|
| + // TODO(paulberry): improve on this.
|
| + rhsType = inferrer.inferExpression(rhs, null, true);
|
| + }
|
| + if (isOverloadedArithmeticOperator) {
|
| + return inferrer.typeSchemaEnvironment
|
| + .getTypeOfOverloadedArithmetic(inferredType, rhsType);
|
| } else {
|
| - inferredType = inferrer
|
| + return inferrer
|
| .getCalleeFunctionType(
|
| - combinerMember, rhsContext, combiner.name, false)
|
| + combinerMember, writeContext, 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();
|
| + return inferrer.inferExpression(rhs, writeContext, true);
|
| }
|
| - 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;
|
| }
|
| }
|
|
|
| @@ -922,6 +909,61 @@ class KernelIfStatement extends IfStatement implements KernelStatement {
|
| }
|
| }
|
|
|
| +/// Concrete shadow object representing an assignment to a target of the form
|
| +/// `a[b]`.
|
| +class KernelIndexAssign extends KernelComplexAssignment {
|
| + /// 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;
|
| +
|
| + KernelIndexAssign(this.receiver, this.index, Expression rhs) : super(rhs);
|
| +
|
| + @override
|
| + List<String> _getToStringParts() {
|
| + var parts = super._getToStringParts();
|
| + if (receiver != null) parts.add('receiver=$receiver');
|
| + if (index != null) parts.add('index=$index');
|
| + return parts;
|
| + }
|
| +
|
| + @override
|
| + DartType _inferExpression(
|
| + KernelTypeInferrer inferrer, DartType typeContext, bool 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 writeContext;
|
| + 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;
|
| + writeContext = calleeType.positionalParameters[1];
|
| + }
|
| + inferrer.inferExpression(index, indexContext, false);
|
| + var inferredType = _inferRhs(inferrer, writeContext);
|
| + inferrer.listener.indexAssignExit(desugared, inferredType);
|
| + return inferredType;
|
| + }
|
| +}
|
| +
|
| /// Common base class for shadow objects representing initializers in kernel
|
| /// form.
|
| abstract class KernelInitializer implements Initializer {
|
| @@ -1893,6 +1935,29 @@ class KernelTypePromoter extends TypePromoterImpl {
|
| }
|
| }
|
|
|
| +/// Concrete shadow object representing an assignment to a local variable.
|
| +class KernelVariableAssignment extends KernelComplexAssignment {
|
| + KernelVariableAssignment(Expression rhs) : super(rhs);
|
| +
|
| + @override
|
| + DartType _inferExpression(
|
| + KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
|
| + typeNeeded =
|
| + inferrer.listener.variableAssignEnter(desugared, typeContext) ||
|
| + typeNeeded;
|
| + // TODO(paulberry): record the appropriate types on let variables and
|
| + // conditional expressions.
|
| + DartType writeContext;
|
| + var write = this.write;
|
| + if (write is VariableSet) {
|
| + writeContext = write.variable.type;
|
| + }
|
| + var inferredType = _inferRhs(inferrer, writeContext);
|
| + inferrer.listener.variableAssignExit(desugared, inferredType);
|
| + return inferredType;
|
| + }
|
| +}
|
| +
|
| /// Concrete shadow object representing a variable declaration in kernel form.
|
| class KernelVariableDeclaration extends VariableDeclaration
|
| implements KernelStatement {
|
|
|