Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(325)

Unified Diff: pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart

Issue 2928033005: Add type inference for assignment to local variables. (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 {

Powered by Google App Engine
This is Rietveld 408576698