blob: 7aaef0157d028c3515b29ce1d562d393573110d7 [file] [log] [blame]
// Copyright (c) 2018, 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.
part of "kernel_shadow_ast.dart";
class InferenceVistor extends BodyVisitor1<void, DartType> {
final ShadowTypeInferrer inferrer;
InferenceVistor(this.inferrer);
@override
void defaultExpression(Expression node, DartType typeContext) {
unhandled("${node.runtimeType}", "InferenceVistor", node.fileOffset,
inferrer.helper.uri);
}
@override
void defaultStatement(Statement node, DartType _) {
unhandled("${node.runtimeType}", "InferenceVistor", node.fileOffset,
inferrer.helper.uri);
}
@override
void visitInvalidExpression(InvalidExpression node, DartType typeContext) {}
@override
void visitIntLiteral(IntLiteral node, DartType typeContext) {}
@override
void visitDoubleLiteral(DoubleLiteral node, DartType typeContext) {}
@override
void visitAsExpression(AsExpression node, DartType typeContext) {
inferrer.inferExpression(
node.operand, const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: true);
}
void visitAssertInitializerJudgment(AssertInitializerJudgment node) {
inferrer.inferStatement(node.judgment);
}
void visitAssertStatementJudgment(AssertStatementJudgment node) {
var conditionJudgment = node.conditionJudgment;
var messageJudgment = node.messageJudgment;
var expectedType = inferrer.coreTypes.boolClass.rawType;
inferrer.inferExpression(
conditionJudgment, expectedType, !inferrer.isTopLevel);
inferrer.ensureAssignable(
expectedType,
getInferredType(conditionJudgment, inferrer),
conditionJudgment,
conditionJudgment.fileOffset);
if (messageJudgment != null) {
inferrer.inferExpression(
messageJudgment, const UnknownType(), !inferrer.isTopLevel);
}
}
@override
void visitAwaitExpression(AwaitExpression node, DartType typeContext) {
if (!inferrer.typeSchemaEnvironment.isEmptyContext(typeContext)) {
typeContext = inferrer.wrapFutureOrType(typeContext);
}
var operand = node.operand;
inferrer.inferExpression(operand, typeContext, true, isVoidAllowed: true);
inferrer.storeInferredType(
node,
inferrer.typeSchemaEnvironment
.unfutureType(getInferredType(operand, inferrer)));
}
void visitBlockJudgment(BlockJudgment node) {
for (var judgment in node.judgments) {
inferrer.inferStatement(judgment);
}
}
@override
void visitBoolLiteral(BoolLiteral node, DartType typeContext) {}
void visitBreakJudgment(BreakJudgment node) {
// No inference needs to be done.
}
void visitContinueJudgment(ContinueJudgment node) {
// No inference needs to be done.
}
void visitCascadeJudgment(CascadeJudgment node, DartType typeContext) {
node.inferredType =
inferrer.inferExpression(node.targetJudgment, typeContext, true);
if (inferrer.strongMode) {
node.variable.type = getInferredType(node, inferrer);
}
for (var judgment in node.cascadeJudgments) {
inferrer.inferExpression(
judgment, const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: true);
}
return null;
}
@override
void visitConditionalExpression(
ConditionalExpression node, DartType typeContext) {
var condition = node.condition;
var then = node.then;
var otherwise = node.otherwise;
var expectedType = inferrer.coreTypes.boolClass.rawType;
inferrer.inferExpression(condition, expectedType, !inferrer.isTopLevel);
inferrer.ensureAssignable(
expectedType,
getInferredType(condition, inferrer),
node.condition,
node.condition.fileOffset);
inferrer.inferExpression(then, typeContext, true, isVoidAllowed: true);
inferrer.inferExpression(otherwise, typeContext, true, isVoidAllowed: true);
DartType inferredType = inferrer.typeSchemaEnvironment
.getStandardUpperBound(getInferredType(then, inferrer),
getInferredType(otherwise, inferrer));
if (inferrer.strongMode) {
node.staticType = inferredType;
}
}
@override
void visitConstructorInvocation(
ConstructorInvocation node, DartType typeContext) {
var library = inferrer.engine.beingInferred[node.target];
if (library != null) {
// There is a cyclic dependency where inferring the types of the
// initializing formals of a constructor required us to infer the
// corresponding field type which required us to know the type of the
// constructor.
String name = node.target.enclosingClass.name;
if (node.target.name.name.isNotEmpty) {
// TODO(ahe): Use `inferrer.helper.constructorNameForDiagnostics`
// instead. However, `inferrer.helper` may be null.
name += ".${node.target.name.name}";
}
library.addProblem(
templateCantInferTypeDueToCircularity.withArguments(name),
node.target.fileOffset,
name.length,
node.target.fileUri);
for (var declaration in node.target.function.positionalParameters) {
declaration.type ??= const DynamicType();
}
for (var declaration in node.target.function.namedParameters) {
declaration.type ??= const DynamicType();
}
} else if ((library = inferrer.engine.toBeInferred[node.target]) != null) {
inferrer.engine.toBeInferred.remove(node.target);
inferrer.engine.beingInferred[node.target] = library;
for (var declaration in node.target.function.positionalParameters) {
inferrer.engine.inferInitializingFormal(declaration, node.target);
}
for (var declaration in node.target.function.namedParameters) {
inferrer.engine.inferInitializingFormal(declaration, node.target);
}
inferrer.engine.beingInferred.remove(node.target);
}
bool hasExplicitTypeArguments =
getExplicitTypeArguments(node.arguments) != null;
var inferenceResult = inferrer.inferInvocation(
typeContext,
node.fileOffset,
node.target.function.functionType,
computeConstructorReturnType(node.target),
node.arguments,
isConst: node.isConst);
inferrer.storeInferredType(node, inferenceResult.type);
KernelLibraryBuilder inferrerLibrary = inferrer.library;
if (!hasExplicitTypeArguments && inferrerLibrary is KernelLibraryBuilder) {
inferrerLibrary.checkBoundsInConstructorInvocation(
node, inferrer.typeSchemaEnvironment,
inferred: true);
}
}
void visitContinueSwitchJudgment(ContinueSwitchJudgment node) {
// No inference needs to be done.
}
void visitDeferredCheckJudgment(
DeferredCheckJudgment node, DartType typeContext) {
// Since the variable is not used in the body we don't need to type infer
// it. We can just type infer the body.
var judgment = node.judgment;
inferrer.inferExpression(judgment, typeContext, true, isVoidAllowed: true);
node.inferredType = getInferredType(judgment, inferrer);
return null;
}
void visitDoJudgment(DoJudgment node) {
var conditionJudgment = node.conditionJudgment;
inferrer.inferStatement(node.bodyJudgment);
var boolType = inferrer.coreTypes.boolClass.rawType;
inferrer.inferExpression(conditionJudgment, boolType, !inferrer.isTopLevel);
inferrer.ensureAssignable(
boolType,
getInferredType(conditionJudgment, inferrer),
node.condition,
node.condition.fileOffset);
}
void visitDoubleJudgment(DoubleJudgment node, DartType typeContext) {
node.inferredType = inferrer.coreTypes.doubleClass.rawType;
return null;
}
void visitEmptyStatementJudgment(EmptyStatementJudgment node) {
// No inference needs to be done.
}
void visitExpressionStatementJudgment(ExpressionStatementJudgment node) {
inferrer.inferExpression(
node.judgment, const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: true);
}
void visitFactoryConstructorInvocationJudgment(
FactoryConstructorInvocationJudgment node, DartType typeContext) {
bool hadExplicitTypeArguments =
getExplicitTypeArguments(node.arguments) != null;
var inferenceResult = inferrer.inferInvocation(
typeContext,
node.fileOffset,
node.target.function.functionType,
computeConstructorReturnType(node.target),
node.argumentJudgments,
isConst: node.isConst);
node.inferredType = inferenceResult.type;
KernelLibraryBuilder inferrerLibrary = inferrer.library;
if (!hadExplicitTypeArguments && inferrerLibrary is KernelLibraryBuilder) {
inferrerLibrary.checkBoundsInFactoryInvocation(
node, inferrer.typeSchemaEnvironment,
inferred: true);
}
return null;
}
void visitShadowFieldInitializer(ShadowFieldInitializer node) {
var initializerType =
inferrer.inferExpression(node.value, node.field.type, true);
inferrer.ensureAssignable(
node.field.type, initializerType, node.value, node.fileOffset);
}
void visitForInJudgment(ForInJudgment node) {
var iterableClass = node.isAsync
? inferrer.coreTypes.streamClass
: inferrer.coreTypes.iterableClass;
DartType context;
bool typeNeeded = false;
bool typeChecksNeeded = !inferrer.isTopLevel;
VariableDeclarationJudgment variable;
var syntheticAssignment = node._syntheticAssignment;
DartType syntheticWriteType;
if (node._declaresVariable) {
variable = node.variableJudgment;
if (inferrer.strongMode && variable._implicitlyTyped) {
typeNeeded = true;
context = const UnknownType();
} else {
context = variable.type;
}
} else if (syntheticAssignment is ComplexAssignmentJudgment) {
syntheticWriteType =
context = syntheticAssignment._getWriteType(inferrer);
} else {
context = const UnknownType();
}
context = inferrer.wrapType(context, iterableClass);
var iterableJudgment = node.iterableJudgment;
inferrer.inferExpression(
iterableJudgment, context, typeNeeded || typeChecksNeeded);
var inferredExpressionType = inferrer
.resolveTypeParameter(getInferredType(iterableJudgment, inferrer));
inferrer.ensureAssignable(
inferrer.wrapType(const DynamicType(), iterableClass),
inferredExpressionType,
node.iterable,
node.iterable.fileOffset,
template: templateForInLoopTypeNotIterable);
DartType inferredType;
if (typeNeeded || typeChecksNeeded) {
inferredType = const DynamicType();
if (inferredExpressionType is InterfaceType) {
InterfaceType supertype = inferrer.classHierarchy
.getTypeAsInstanceOf(inferredExpressionType, iterableClass);
if (supertype != null) {
inferredType = supertype.typeArguments[0];
}
}
if (typeNeeded) {
inferrer.instrumentation?.record(inferrer.uri, variable.fileOffset,
'type', new InstrumentationValueForType(inferredType));
variable.type = inferredType;
}
if (!node._declaresVariable) {
node.variable.type = inferredType;
}
}
inferrer.inferStatement(node.bodyJudgment);
if (syntheticAssignment != null) {
var syntheticStatement = new ExpressionStatement(syntheticAssignment);
node.body = combineStatements(syntheticStatement, node.body)
..parent = node;
}
if (node._declaresVariable) {
inferrer.inferMetadataKeepingHelper(variable.annotations);
var tempVar =
new VariableDeclaration(null, type: inferredType, isFinal: true);
var variableGet = new VariableGet(tempVar)
..fileOffset = node.variable.fileOffset;
var implicitDowncast = inferrer.ensureAssignable(
variable.type, inferredType, variableGet, node.fileOffset,
template: templateForInLoopElementTypeNotAssignable);
if (implicitDowncast != null) {
node.variable = tempVar..parent = node;
variable.initializer = implicitDowncast..parent = variable;
node.body = combineStatements(variable, node.body)..parent = node;
}
} else if (syntheticAssignment is SyntheticExpressionJudgment) {
if (syntheticAssignment is ComplexAssignmentJudgment) {
inferrer.ensureAssignable(
greatestClosure(inferrer.coreTypes, syntheticWriteType),
node.variable.type,
syntheticAssignment.rhs,
syntheticAssignment.rhs.fileOffset,
template: templateForInLoopElementTypeNotAssignable,
isVoidAllowed: true);
if (syntheticAssignment is PropertyAssignmentJudgment) {
syntheticAssignment._handleWriteContravariance(
inferrer, inferrer.thisType);
}
}
syntheticAssignment._replaceWithDesugared();
}
}
void visitForJudgment(ForJudgment node) {
var initializers = node.initializers;
var conditionJudgment = node.conditionJudgment;
if (initializers != null) {
for (var initializer in initializers) {
node.variables
.add(new VariableDeclaration.forValue(initializer)..parent = node);
inferrer.inferExpression(
initializer, const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: true);
}
} else {
for (var variable in node.variableJudgments) {
inferrer.inferStatement(variable);
}
}
if (conditionJudgment != null) {
var expectedType = inferrer.coreTypes.boolClass.rawType;
inferrer.inferExpression(
conditionJudgment, expectedType, !inferrer.isTopLevel);
inferrer.ensureAssignable(
expectedType,
getInferredType(conditionJudgment, inferrer),
node.condition,
node.condition.fileOffset);
}
for (var update in node.updateJudgments) {
inferrer.inferExpression(
update, const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: true);
}
inferrer.inferStatement(node.bodyJudgment);
}
ExpressionInferenceResult visitFunctionNodeJudgment(
FunctionNodeJudgment node,
DartType typeContext,
DartType returnContext,
int returnTypeInstrumentationOffset) {
return inferrer.inferLocalFunction(
node, typeContext, returnTypeInstrumentationOffset, returnContext);
}
void visitFunctionDeclarationJudgment(FunctionDeclarationJudgment node) {
inferrer.inferMetadataKeepingHelper(node.variable.annotations);
DartType returnContext = node._hasImplicitReturnType
? (inferrer.strongMode ? null : const DynamicType())
: node.function.returnType;
var inferenceResult = visitFunctionNodeJudgment(
node.functionJudgment, null, returnContext, node.fileOffset);
node.variable.type = inferenceResult.type;
}
@override
void visitFunctionExpression(FunctionExpression node, DartType typeContext) {
var inferenceResult = visitFunctionNodeJudgment(
node.function, typeContext, null, node.fileOffset);
inferrer.storeInferredType(node, inferenceResult.type);
}
void visitInvalidSuperInitializerJudgment(
InvalidSuperInitializerJudgment node) {
var substitution = Substitution.fromSupertype(inferrer.classHierarchy
.getClassAsInstanceOf(
inferrer.thisType.classNode, node.target.enclosingClass));
inferrer.inferInvocation(
null,
node.fileOffset,
substitution.substituteType(
node.target.function.functionType.withoutTypeParameters),
inferrer.thisType,
node.argumentsJudgment,
skipTypeArgumentInference: true);
}
void visitIfNullJudgment(IfNullJudgment node, DartType typeContext) {
var leftJudgment = node.leftJudgment;
var rightJudgment = node.rightJudgment;
// To infer `e0 ?? e1` in context K:
// - Infer e0 in context K to get T0
inferrer.inferExpression(leftJudgment, typeContext, true);
var lhsType = getInferredType(leftJudgment, inferrer);
if (inferrer.strongMode) {
node.variable.type = lhsType;
}
// - Let J = T0 if K is `?` else K.
// - Infer e1 in context J to get T1
if (typeContext is UnknownType) {
inferrer.inferExpression(rightJudgment, lhsType, true,
isVoidAllowed: true);
} else {
inferrer.inferExpression(rightJudgment, typeContext, true,
isVoidAllowed: true);
}
var rhsType = getInferredType(rightJudgment, inferrer);
// - Let T = greatest closure of K with respect to `?` if K is not `_`, else
// UP(t0, t1)
// - Then the inferred type is T.
node.inferredType =
inferrer.typeSchemaEnvironment.getStandardUpperBound(lhsType, rhsType);
if (inferrer.strongMode) {
node.body.staticType = getInferredType(node, inferrer);
}
return null;
}
void visitIfJudgment(IfJudgment node) {
var conditionJudgment = node.conditionJudgment;
var expectedType = inferrer.coreTypes.boolClass.rawType;
inferrer.inferExpression(
conditionJudgment, expectedType, !inferrer.isTopLevel);
inferrer.ensureAssignable(
expectedType,
getInferredType(conditionJudgment, inferrer),
node.condition,
node.condition.fileOffset);
inferrer.inferStatement(node.thenJudgment);
if (node.otherwiseJudgment != null) {
inferrer.inferStatement(node.otherwiseJudgment);
}
}
void visitIllegalAssignmentJudgment(
IllegalAssignmentJudgment node, DartType typeContext) {
if (node.write != null) {
inferrer.inferExpression(
node.write, const UnknownType(), !inferrer.isTopLevel);
}
inferrer.inferExpression(
node.rhs, const UnknownType(), !inferrer.isTopLevel);
node._replaceWithDesugared();
node.inferredType = const DynamicType();
return null;
}
void visitIndexAssignmentJudgment(
IndexAssignmentJudgment node, DartType typeContext) {
var receiverType = node._inferReceiver(inferrer);
var writeMember =
inferrer.findMethodInvocationMember(receiverType, node.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.getCalleeFunctionType(
inferrer.getCalleeType(writeMember, receiverType), false);
DartType expectedIndexTypeForWrite;
DartType indexContext = const UnknownType();
DartType writeContext = const UnknownType();
if (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.
expectedIndexTypeForWrite = calleeType.positionalParameters[0];
writeContext = calleeType.positionalParameters[1];
}
inferrer.inferExpression(node.index, indexContext, true);
var indexType = getInferredType(node.index, inferrer);
node._storeLetType(inferrer, node.index, indexType);
if (writeContext is! UnknownType) {
inferrer.ensureAssignable(
expectedIndexTypeForWrite,
indexType,
node._getInvocationArguments(inferrer, node.write).positional[0],
node.write.fileOffset);
}
InvocationExpression read = node.read;
DartType readType;
if (read != null) {
var readMember = inferrer.findMethodInvocationMember(receiverType, read,
instrumented: false);
var calleeFunctionType = inferrer.getCalleeFunctionType(
inferrer.getCalleeType(readMember, receiverType), false);
inferrer.ensureAssignable(
getPositionalParameterType(calleeFunctionType, 0),
indexType,
node._getInvocationArguments(inferrer, read).positional[0],
read.fileOffset);
readType = calleeFunctionType.returnType;
var desugaredInvocation = read is MethodInvocation ? read : null;
var checkKind = inferrer.preCheckInvocationContravariance(node.receiver,
receiverType, readMember, desugaredInvocation, read.arguments, read);
var replacedRead = inferrer.handleInvocationContravariance(
checkKind,
desugaredInvocation,
read.arguments,
read,
readType,
calleeFunctionType,
read.fileOffset);
node._storeLetType(inferrer, replacedRead, readType);
}
node._inferRhs(inferrer, readType, writeContext);
node._replaceWithDesugared();
return null;
}
void visitIntJudgment(IntJudgment node, DartType typeContext) {
if (inferrer.isDoubleContext(typeContext)) {
double doubleValue = node.asDouble();
if (doubleValue != null) {
node.parent.replaceChild(
node, DoubleLiteral(doubleValue)..fileOffset = node.fileOffset);
node.inferredType = inferrer.coreTypes.doubleClass.rawType;
return null;
}
}
Expression error = checkWebIntLiteralsErrorIfUnexact(
inferrer, node.value, node.literal, node.fileOffset);
if (error != null) {
node.parent.replaceChild(node, error);
node.inferredType = const BottomType();
return null;
}
node.inferredType = inferrer.coreTypes.intClass.rawType;
return null;
}
void visitShadowLargeIntLiteral(
ShadowLargeIntLiteral node, DartType typeContext) {
if (inferrer.isDoubleContext(typeContext)) {
double doubleValue = node.asDouble();
if (doubleValue != null) {
node.parent.replaceChild(
node, DoubleLiteral(doubleValue)..fileOffset = node.fileOffset);
node.inferredType = inferrer.coreTypes.doubleClass.rawType;
return null;
}
}
int intValue = node.asInt64();
if (intValue == null) {
Expression replacement = inferrer.helper
.buildProblem(
templateIntegerLiteralIsOutOfRange.withArguments(node.literal),
node.fileOffset,
node.literal.length)
.desugared;
node.parent.replaceChild(node, replacement);
node.inferredType = const BottomType();
return null;
}
Expression error = checkWebIntLiteralsErrorIfUnexact(
inferrer, intValue, node.literal, node.fileOffset);
if (error != null) {
node.parent.replaceChild(node, error);
node.inferredType = const BottomType();
return null;
}
node.parent
.replaceChild(node, IntLiteral(intValue)..fileOffset = node.fileOffset);
node.inferredType = inferrer.coreTypes.intClass.rawType;
return null;
}
void visitShadowInvalidInitializer(ShadowInvalidInitializer node) {
inferrer.inferExpression(
node.variable.initializer, const UnknownType(), !inferrer.isTopLevel);
}
void visitShadowInvalidFieldInitializer(ShadowInvalidFieldInitializer node) {
inferrer.inferExpression(node.value, node.field.type, !inferrer.isTopLevel);
}
@override
void visitIsExpression(IsExpression node, DartType typeContext) {
inferrer.inferExpression(
node.operand, const UnknownType(), !inferrer.isTopLevel);
}
void visitLabeledStatementJudgment(LabeledStatementJudgment node) {
inferrer.inferStatement(node.judgment);
}
void visitListLiteralJudgment(
ListLiteralJudgment node, DartType typeContext) {
var listClass = inferrer.coreTypes.listClass;
var listType = listClass.thisType;
List<DartType> inferredTypes;
DartType inferredTypeArgument;
List<DartType> formalTypes;
List<DartType> actualTypes;
bool inferenceNeeded =
node._declaredTypeArgument == null && inferrer.strongMode;
bool typeChecksNeeded = !inferrer.isTopLevel;
if (inferenceNeeded || typeChecksNeeded) {
formalTypes = [];
actualTypes = [];
}
if (inferenceNeeded) {
inferredTypes = [const UnknownType()];
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(listType,
listClass.typeParameters, null, null, typeContext, inferredTypes,
isConst: node.isConst);
inferredTypeArgument = inferredTypes[0];
} else {
inferredTypeArgument = node._declaredTypeArgument ?? const DynamicType();
}
if (inferenceNeeded || typeChecksNeeded) {
for (int i = 0; i < node.judgments.length; ++i) {
Expression judgment = node.judgments[i];
inferrer.inferExpression(
judgment, inferredTypeArgument, inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
if (inferenceNeeded) {
formalTypes.add(listType.typeArguments[0]);
}
actualTypes.add(getInferredType(judgment, inferrer));
}
}
if (inferenceNeeded) {
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(
listType,
listClass.typeParameters,
formalTypes,
actualTypes,
typeContext,
inferredTypes);
inferredTypeArgument = inferredTypes[0];
inferrer.instrumentation?.record(
inferrer.uri,
node.fileOffset,
'typeArgs',
new InstrumentationValueForTypeArgs([inferredTypeArgument]));
node.typeArgument = inferredTypeArgument;
}
if (typeChecksNeeded) {
for (int i = 0; i < node.judgments.length; i++) {
inferrer.ensureAssignable(node.typeArgument, actualTypes[i],
node.judgments[i], node.judgments[i].fileOffset,
isVoidAllowed: node.typeArgument is VoidType);
}
}
node.inferredType = new InterfaceType(listClass, [inferredTypeArgument]);
KernelLibraryBuilder inferrerLibrary = inferrer.library;
if (node._declaredTypeArgument == null &&
inferrerLibrary is KernelLibraryBuilder) {
inferrerLibrary.checkBoundsInListLiteral(
node, inferrer.typeSchemaEnvironment,
inferred: true);
}
return null;
}
@override
void visitLogicalExpression(LogicalExpression node, DartType typeContext) {
var boolType = inferrer.coreTypes.boolClass.rawType;
var left = node.left;
var right = node.right;
inferrer.inferExpression(left, boolType, !inferrer.isTopLevel);
inferrer.inferExpression(right, boolType, !inferrer.isTopLevel);
inferrer.ensureAssignable(boolType, getInferredType(left, inferrer),
node.left, node.left.fileOffset);
inferrer.ensureAssignable(boolType, getInferredType(right, inferrer),
node.right, node.right.fileOffset);
return null;
}
void visitMapEntryJudgment(MapEntryJudgment node, DartType keyTypeContext,
DartType valueTypeContext) {
Expression keyJudgment = node.keyJudgment;
inferrer.inferExpression(keyJudgment, keyTypeContext, true,
isVoidAllowed: true);
node.inferredKeyType = getInferredType(keyJudgment, inferrer);
Expression valueJudgment = node.valueJudgment;
inferrer.inferExpression(valueJudgment, valueTypeContext, true,
isVoidAllowed: true);
node.inferredValueType = getInferredType(valueJudgment, inferrer);
return null;
}
void visitMapLiteralJudgment(MapLiteralJudgment node, DartType typeContext) {
var mapClass = inferrer.coreTypes.mapClass;
var mapType = mapClass.thisType;
List<DartType> inferredTypes;
DartType inferredKeyType;
DartType inferredValueType;
List<DartType> formalTypes;
List<DartType> actualTypes;
assert(
(node._declaredKeyType == null) == (node._declaredValueType == null));
bool inferenceNeeded = node._declaredKeyType == null && inferrer.strongMode;
bool typeChecksNeeded = !inferrer.isTopLevel;
if (inferenceNeeded || typeChecksNeeded) {
formalTypes = [];
actualTypes = [];
}
if (inferenceNeeded) {
inferredTypes = [const UnknownType(), const UnknownType()];
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(mapType,
mapClass.typeParameters, null, null, typeContext, inferredTypes,
isConst: node.isConst);
inferredKeyType = inferredTypes[0];
inferredValueType = inferredTypes[1];
} else {
inferredKeyType = node._declaredKeyType ?? const DynamicType();
inferredValueType = node._declaredValueType ?? const DynamicType();
}
List<Expression> cachedKeyJudgments =
node.judgments.map((j) => (j as MapEntryJudgment).keyJudgment).toList();
List<Expression> cachedValueJudgments = node.judgments
.map((j) => (j as MapEntryJudgment).valueJudgment)
.toList();
if (inferenceNeeded || typeChecksNeeded) {
for (MapEntryJudgment judgment in node.judgments) {
visitMapEntryJudgment(judgment, inferredKeyType, inferredValueType);
if (inferenceNeeded) {
formalTypes.addAll(mapType.typeArguments);
}
actualTypes.add(judgment.inferredKeyType);
actualTypes.add(judgment.inferredValueType);
}
}
if (inferenceNeeded) {
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(
mapType,
mapClass.typeParameters,
formalTypes,
actualTypes,
typeContext,
inferredTypes);
inferredKeyType = inferredTypes[0];
inferredValueType = inferredTypes[1];
inferrer.instrumentation?.record(
inferrer.uri,
node.fileOffset,
'typeArgs',
new InstrumentationValueForTypeArgs(
[inferredKeyType, inferredValueType]));
node.keyType = inferredKeyType;
node.valueType = inferredValueType;
}
if (typeChecksNeeded) {
for (int i = 0; i < node.judgments.length; ++i) {
Expression keyJudgment = cachedKeyJudgments[i];
inferrer.ensureAssignable(node.keyType, actualTypes[2 * i], keyJudgment,
keyJudgment.fileOffset,
isVoidAllowed: node.keyType is VoidType);
Expression valueJudgment = cachedValueJudgments[i];
inferrer.ensureAssignable(node.valueType, actualTypes[2 * i + 1],
valueJudgment, valueJudgment.fileOffset,
isVoidAllowed: node.valueType is VoidType);
}
}
node.inferredType =
new InterfaceType(mapClass, [inferredKeyType, inferredValueType]);
KernelLibraryBuilder inferrerLibrary = inferrer.library;
// Either both [_declaredKeyType] and [_declaredValueType] are omitted or
// none of them, so we may just check one.
if (node._declaredKeyType == null &&
inferrerLibrary is KernelLibraryBuilder) {
inferrerLibrary.checkBoundsInMapLiteral(
node, inferrer.typeSchemaEnvironment,
inferred: true);
}
return null;
}
void visitMethodInvocationJudgment(
MethodInvocationJudgment node, DartType typeContext) {
if (node.name.name == 'unary-' &&
node.arguments.types.isEmpty &&
node.arguments.positional.isEmpty &&
node.arguments.named.isEmpty) {
// Replace integer literals in a double context with the corresponding
// double literal if it's exact. For double literals, the negation is
// folded away. In any non-double context, or if there is no exact
// double value, then the corresponding integer literal is left. The
// negation is not folded away so that platforms with web literals can
// distinguish between (non-negated) 0x8000000000000000 represented as
// integer literal -9223372036854775808 which should be a positive number,
// and negated 9223372036854775808 represented as
// -9223372036854775808.unary-() which should be a negative number.
if (node.receiver is IntJudgment) {
IntJudgment receiver = node.receiver;
if (inferrer.isDoubleContext(typeContext)) {
double doubleValue = receiver.asDouble(negated: true);
if (doubleValue != null) {
node.parent.replaceChild(
node, DoubleLiteral(doubleValue)..fileOffset = node.fileOffset);
node.inferredType = inferrer.coreTypes.doubleClass.rawType;
return null;
}
}
Expression error = checkWebIntLiteralsErrorIfUnexact(
inferrer, receiver.value, receiver.literal, receiver.fileOffset);
if (error != null) {
node.parent.replaceChild(node, error);
node.inferredType = const BottomType();
return null;
}
} else if (node.receiver is ShadowLargeIntLiteral) {
ShadowLargeIntLiteral receiver = node.receiver;
if (!receiver.isParenthesized) {
if (inferrer.isDoubleContext(typeContext)) {
double doubleValue = receiver.asDouble(negated: true);
if (doubleValue != null) {
node.parent.replaceChild(node,
DoubleLiteral(doubleValue)..fileOffset = node.fileOffset);
node.inferredType = inferrer.coreTypes.doubleClass.rawType;
return null;
}
}
int intValue = receiver.asInt64(negated: true);
if (intValue == null) {
Expression error = inferrer.helper
.buildProblem(
templateIntegerLiteralIsOutOfRange
.withArguments(receiver.literal),
receiver.fileOffset,
receiver.literal.length)
.desugared;
node.parent.replaceChild(node, error);
node.inferredType = const BottomType();
return null;
}
if (intValue != null) {
Expression error = checkWebIntLiteralsErrorIfUnexact(
inferrer, intValue, receiver.literal, receiver.fileOffset);
if (error != null) {
node.parent.replaceChild(node, error);
node.inferredType = const BottomType();
return null;
}
node.receiver = IntLiteral(-intValue)
..fileOffset = node.receiver.fileOffset
..parent = node;
}
}
}
}
var inferenceResult = inferrer.inferMethodInvocation(
node, node.receiver, node.fileOffset, node._isImplicitCall, typeContext,
desugaredInvocation: node);
node.inferredType = inferenceResult.type;
}
void visitNamedFunctionExpressionJudgment(
NamedFunctionExpressionJudgment node, DartType typeContext) {
Expression initializer = node.variableJudgment.initializer;
inferrer.inferExpression(initializer, typeContext, true);
node.inferredType = getInferredType(initializer, inferrer);
if (inferrer.strongMode) node.variable.type = node.inferredType;
return null;
}
@override
void visitNot(Not node, DartType typeContext) {
var operand = node.operand;
var boolType = inferrer.coreTypes.boolClass.rawType;
inferrer.inferExpression(operand, boolType, !inferrer.isTopLevel);
inferrer.ensureAssignable(boolType, getInferredType(operand, inferrer),
node.operand, node.fileOffset);
}
void visitNullAwareMethodInvocationJudgment(
NullAwareMethodInvocationJudgment node, DartType typeContext) {
var inferenceResult = inferrer.inferMethodInvocation(
node, node.variable.initializer, node.fileOffset, false, typeContext,
receiverVariable: node.variable,
desugaredInvocation: node._desugaredInvocation);
node.inferredType = inferenceResult.type;
if (inferrer.strongMode) {
node.body.staticType = node.inferredType;
}
return null;
}
void visitNullAwarePropertyGetJudgment(
NullAwarePropertyGetJudgment node, DartType typeContext) {
inferrer.inferPropertyGet(
node, node.receiverJudgment, node.fileOffset, typeContext,
receiverVariable: node.variable, desugaredGet: node._desugaredGet);
if (inferrer.strongMode) {
node.body.staticType = node.inferredType;
}
return null;
}
@override
void visitNullLiteral(NullLiteral node, DartType typeContext) {}
@override
void visitLet(Let node, DartType typeContext) {
DartType variableType = node.variable.type;
if (variableType == const DynamicType()) {
return defaultExpression(node, typeContext);
}
Expression initializer = node.variable.initializer;
inferrer.inferExpression(initializer, variableType, true,
isVoidAllowed: true);
Expression body = node.body;
inferrer.inferExpression(body, typeContext, true, isVoidAllowed: true);
// TODO(ahe): This shouldn't be needed. See InferredTypeVisitor.visitLet.
inferrer.storeInferredType(node, getInferredType(body, inferrer));
}
void visitPropertyAssignmentJudgment(
PropertyAssignmentJudgment node, DartType typeContext) {
var receiverType = node._inferReceiver(inferrer);
DartType readType;
if (node.read != null) {
var readMember = inferrer.findPropertyGetMember(receiverType, node.read,
instrumented: false);
readType = inferrer.getCalleeType(readMember, receiverType);
inferrer.handlePropertyGetContravariance(
node.receiver,
readMember,
node.read is PropertyGet ? node.read : null,
node.read,
readType,
node.read.fileOffset);
node._storeLetType(inferrer, node.read, readType);
}
Member writeMember;
if (node.write != null) {
writeMember = node._handleWriteContravariance(inferrer, receiverType);
}
// 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 writeContext = inferrer.getSetterType(writeMember, receiverType);
node._inferRhs(inferrer, readType, writeContext);
if (inferrer.strongMode)
node.nullAwareGuard?.staticType = node.inferredType;
node._replaceWithDesugared();
return null;
}
@override
void visitPropertyGet(PropertyGet node, DartType typeContext) {
inferrer.inferPropertyGet(node, node.receiver, node.fileOffset, typeContext,
desugaredGet: node);
}
void visitRedirectingInitializerJudgment(
RedirectingInitializerJudgment node) {
List<TypeParameter> classTypeParameters =
node.target.enclosingClass.typeParameters;
List<DartType> typeArguments =
new List<DartType>(classTypeParameters.length);
for (int i = 0; i < typeArguments.length; i++) {
typeArguments[i] = new TypeParameterType(classTypeParameters[i]);
}
ArgumentsJudgment.setNonInferrableArgumentTypes(
node.arguments, typeArguments);
inferrer.inferInvocation(
null,
node.fileOffset,
node.target.function.functionType,
node.target.enclosingClass.thisType,
node.argumentJudgments,
skipTypeArgumentInference: true);
ArgumentsJudgment.removeNonInferrableArgumentTypes(node.arguments);
}
@override
void visitRethrow(Rethrow node, DartType typeContext) {}
void visitReturnJudgment(ReturnJudgment node) {
var judgment = node.judgment;
var closureContext = inferrer.closureContext;
DartType typeContext = !closureContext.isGenerator
? closureContext.returnOrYieldContext
: const UnknownType();
DartType inferredType;
if (node.expression != null) {
inferrer.inferExpression(judgment, typeContext, true,
isVoidAllowed: true);
inferredType = getInferredType(judgment, inferrer);
} else {
inferredType = inferrer.coreTypes.nullClass.rawType;
}
closureContext.handleReturn(inferrer, node, inferredType,
!identical(node.returnKeywordLexeme, "return"));
}
void visitStaticAssignmentJudgment(
StaticAssignmentJudgment node, DartType typeContext) {
DartType readType = const DynamicType(); // Only used in error recovery
var read = node.read;
if (read is StaticGet) {
readType = read.target.getterType;
node._storeLetType(inferrer, read, readType);
}
Member writeMember;
DartType writeContext = const UnknownType();
var write = node.write;
if (write is StaticSet) {
writeContext = write.target.setterType;
writeMember = write.target;
if (writeMember is ShadowField && writeMember.inferenceNode != null) {
writeMember.inferenceNode.resolve();
writeMember.inferenceNode = null;
}
}
node._inferRhs(inferrer, readType, writeContext);
node._replaceWithDesugared();
return null;
}
@override
void visitStaticGet(StaticGet node, DartType typeContext) {
var target = node.target;
if (target is ShadowField && target.inferenceNode != null) {
target.inferenceNode.resolve();
target.inferenceNode = null;
}
var type = target.getterType;
if (target is Procedure && target.kind == ProcedureKind.Method) {
type = inferrer.instantiateTearOff(type, typeContext, node);
}
inferrer.storeInferredType(node, type);
}
@override
void visitStaticInvocation(StaticInvocation node, DartType typeContext) {
FunctionType calleeType = node.target != null
? node.target.function.functionType
: new FunctionType([], const DynamicType());
bool hadExplicitTypeArguments =
getExplicitTypeArguments(node.arguments) != null;
var inferenceResult = inferrer.inferInvocation(typeContext, node.fileOffset,
calleeType, calleeType.returnType, node.arguments);
inferrer.storeInferredType(node, inferenceResult.type);
if (!hadExplicitTypeArguments && node.target != null) {
inferrer.library?.checkBoundsInStaticInvocation(
node, inferrer.typeSchemaEnvironment,
inferred: true);
}
}
@override
void visitStringConcatenation(
StringConcatenation node, DartType typeContext) {
if (!inferrer.isTopLevel) {
for (var expression in node.expressions) {
inferrer.inferExpression(
expression, const UnknownType(), !inferrer.isTopLevel);
}
}
}
@override
void visitStringLiteral(StringLiteral node, DartType typeContext) {}
void visitSuperInitializerJudgment(SuperInitializerJudgment node) {
var substitution = Substitution.fromSupertype(inferrer.classHierarchy
.getClassAsInstanceOf(
inferrer.thisType.classNode, node.target.enclosingClass));
inferrer.inferInvocation(
null,
node.fileOffset,
substitution.substituteType(
node.target.function.functionType.withoutTypeParameters),
inferrer.thisType,
node.argumentJudgments,
skipTypeArgumentInference: true);
}
void visitSuperMethodInvocationJudgment(
SuperMethodInvocationJudgment node, DartType typeContext) {
if (node.interfaceTarget != null) {
inferrer.instrumentation?.record(inferrer.uri, node.fileOffset, 'target',
new InstrumentationValueForMember(node.interfaceTarget));
}
var inferenceResult = inferrer.inferMethodInvocation(
node, null, node.fileOffset, false, typeContext,
interfaceMember: node.interfaceTarget,
methodName: node.name,
arguments: node.arguments);
node.inferredType = inferenceResult.type;
}
void visitSuperPropertyGetJudgment(
SuperPropertyGetJudgment node, DartType typeContext) {
if (node.interfaceTarget != null) {
inferrer.instrumentation?.record(inferrer.uri, node.fileOffset, 'target',
new InstrumentationValueForMember(node.interfaceTarget));
}
inferrer.inferPropertyGet(node, null, node.fileOffset, typeContext,
interfaceMember: node.interfaceTarget, propertyName: node.name);
}
void visitSwitchStatementJudgment(SwitchStatementJudgment node) {
var expressionJudgment = node.expressionJudgment;
inferrer.inferExpression(expressionJudgment, const UnknownType(), true);
var expressionType = getInferredType(expressionJudgment, inferrer);
for (var switchCase in node.caseJudgments) {
for (var caseExpression in switchCase.expressionJudgments) {
DartType caseExpressionType =
inferrer.inferExpression(caseExpression, expressionType, true);
// Check whether the expression type is assignable to the case expression type.
if (!inferrer.isAssignable(expressionType, caseExpressionType)) {
inferrer.helper.addProblem(
templateSwitchExpressionNotAssignable.withArguments(
expressionType, caseExpressionType),
caseExpression.fileOffset,
noLength,
context: [
messageSwitchExpressionNotAssignableCause.withLocation(
inferrer.uri, expressionJudgment.fileOffset, noLength)
]);
}
}
inferrer.inferStatement(switchCase.bodyJudgment);
}
}
void visitSymbolLiteralJudgment(
SymbolLiteralJudgment node, DartType typeContext) {
node.inferredType = inferrer.coreTypes.symbolClass.rawType;
return null;
}
void visitInvalidConstructorInvocationJudgment(
InvalidConstructorInvocationJudgment node, DartType typeContext) {
FunctionType calleeType;
DartType returnType;
if (node.constructor != null) {
calleeType = node.constructor.function.functionType;
returnType = computeConstructorReturnType(node.constructor);
} else {
calleeType = new FunctionType([], const DynamicType());
returnType = const DynamicType();
}
ExpressionInferenceResult inferenceResult = inferrer.inferInvocation(
typeContext,
node.fileOffset,
calleeType,
returnType,
node.argumentJudgments);
node.inferredType = inferenceResult.type;
return visitSyntheticExpressionJudgment(node, typeContext);
}
void visitInvalidWriteJudgment(
InvalidWriteJudgment node, DartType typeContext) {
// When a compound assignment, the expression is already wrapping in
// VariableDeclaration in _makeRead(). Otherwise, temporary associate
// the expression with this node.
node.expression.parent ??= node;
inferrer.inferExpression(
node.expression, const UnknownType(), !inferrer.isTopLevel);
return visitSyntheticExpressionJudgment(node, typeContext);
}
void visitSyntheticExpressionJudgment(
SyntheticExpressionJudgment node, DartType typeContext) {
node._replaceWithDesugared();
node.inferredType = const DynamicType();
return null;
}
void visitThisExpression(ThisExpression node, DartType typeContext) {}
@override
void visitThrow(Throw node, DartType typeContext) {
inferrer.inferExpression(
node.expression, const UnknownType(), !inferrer.isTopLevel);
}
void visitCatchJudgment(CatchJudgment node) {
inferrer.inferStatement(node.bodyJudgment);
}
void visitTryCatchJudgment(TryCatchJudgment node) {
inferrer.inferStatement(node.bodyJudgment);
for (var catch_ in node.catchJudgments) {
visitCatchJudgment(catch_);
}
}
void visitTryFinallyJudgment(TryFinallyJudgment node) {
inferrer.inferStatement(node.body);
if (node.catchJudgments != null) {
for (var catch_ in node.catchJudgments) {
visitCatchJudgment(catch_);
}
node.body = new TryCatch(node.body, node.catches)..parent = node;
}
inferrer.inferStatement(node.finalizerJudgment);
}
void visitTypeLiteralJudgment(
TypeLiteralJudgment node, DartType typeContext) {
node.inferredType = inferrer.coreTypes.typeClass.rawType;
return null;
}
void visitVariableAssignmentJudgment(
VariableAssignmentJudgment node, DartType typeContext) {
DartType readType;
var read = node.read;
if (read is VariableGet) {
readType = read.promotedType ?? read.variable.type;
}
DartType writeContext = const UnknownType();
var write = node.write;
if (write is VariableSet) {
writeContext = write.variable.type;
if (read != null) {
node._storeLetType(inferrer, read, writeContext);
}
}
node._inferRhs(inferrer, readType, writeContext);
node._replaceWithDesugared();
return null;
}
void visitVariableDeclarationJudgment(VariableDeclarationJudgment node) {
if (node.annotationJudgments.isNotEmpty) {
if (node.infersAnnotations) {
inferrer.inferMetadataKeepingHelper(node.annotationJudgments);
}
// After the inference was done on the annotations, we may clone them for
// this instance of VariableDeclaration in order to avoid having the same
// annotation node for two VariableDeclaration nodes in a situation like
// the following:
//
// class Foo { const Foo(List<String> list); }
//
// @Foo(const [])
// var x, y;
CloneVisitor cloner = new CloneVisitor();
for (int i = 0; i < node.annotations.length; ++i) {
kernel.Expression annotation = node.annotations[i];
if (annotation.parent != node) {
node.annotations[i] = cloner.clone(annotation);
node.annotations[i].parent = node;
}
}
}
var initializerJudgment = node.initializerJudgment;
var declaredType = node._implicitlyTyped ? const UnknownType() : node.type;
DartType inferredType;
DartType initializerType;
if (initializerJudgment != null) {
inferrer.inferExpression(initializerJudgment, declaredType,
!inferrer.isTopLevel || node._implicitlyTyped,
isVoidAllowed: true);
initializerType = getInferredType(initializerJudgment, inferrer);
inferredType = inferrer.inferDeclarationType(initializerType);
} else {
inferredType = const DynamicType();
}
if (inferrer.strongMode && node._implicitlyTyped) {
inferrer.instrumentation?.record(inferrer.uri, node.fileOffset, 'type',
new InstrumentationValueForType(inferredType));
node.type = inferredType;
}
if (node.initializer != null) {
var replacedInitializer = inferrer.ensureAssignable(
node.type, initializerType, node.initializer, node.fileOffset,
isVoidAllowed: node.type is VoidType);
if (replacedInitializer != null) {
node.initializer = replacedInitializer;
}
}
KernelLibraryBuilder inferrerLibrary = inferrer.library;
if (node._implicitlyTyped && inferrerLibrary is KernelLibraryBuilder) {
inferrerLibrary.checkBoundsInVariableDeclaration(
node, inferrer.typeSchemaEnvironment,
inferred: true);
}
}
void visitUnresolvedTargetInvocationJudgment(
UnresolvedTargetInvocationJudgment node, DartType typeContext) {
var result = visitSyntheticExpressionJudgment(node, typeContext);
inferrer.inferInvocation(
typeContext,
node.fileOffset,
TypeInferrerImpl.unknownFunction,
const DynamicType(),
node.argumentsJudgment);
return result;
}
void visitUnresolvedVariableAssignmentJudgment(
UnresolvedVariableAssignmentJudgment node, DartType typeContext) {
inferrer.inferExpression(node.rhs, const UnknownType(), true);
node.inferredType = node.isCompound
? const DynamicType()
: getInferredType(node.rhs, inferrer);
return visitSyntheticExpressionJudgment(node, typeContext);
}
void visitVariableGetJudgment(
VariableGetJudgment node, DartType typeContext) {
VariableDeclarationJudgment variable = node.variable;
bool mutatedInClosure = variable._mutatedInClosure;
DartType declaredOrInferredType = variable.type;
DartType promotedType = inferrer.typePromoter
.computePromotedType(node._fact, node._scope, mutatedInClosure);
if (promotedType != null) {
inferrer.instrumentation?.record(inferrer.uri, node.fileOffset,
'promotedType', new InstrumentationValueForType(promotedType));
}
node.promotedType = promotedType;
var type = promotedType ?? declaredOrInferredType;
if (variable._isLocalFunction) {
type = inferrer.instantiateTearOff(type, typeContext, node);
}
node.inferredType = type;
return null;
}
void visitWhileJudgment(WhileJudgment node) {
var conditionJudgment = node.conditionJudgment;
var expectedType = inferrer.coreTypes.boolClass.rawType;
inferrer.inferExpression(
conditionJudgment, expectedType, !inferrer.isTopLevel);
inferrer.ensureAssignable(
expectedType,
getInferredType(conditionJudgment, inferrer),
node.condition,
node.condition.fileOffset);
inferrer.inferStatement(node.bodyJudgment);
}
void visitYieldJudgment(YieldJudgment node) {
var judgment = node.judgment;
var closureContext = inferrer.closureContext;
if (closureContext.isGenerator) {
var typeContext = closureContext.returnOrYieldContext;
if (node.isYieldStar && typeContext != null) {
typeContext = inferrer.wrapType(
typeContext,
closureContext.isAsync
? inferrer.coreTypes.streamClass
: inferrer.coreTypes.iterableClass);
}
inferrer.inferExpression(judgment, typeContext, true);
} else {
inferrer.inferExpression(judgment, const UnknownType(), true);
}
closureContext.handleYield(inferrer, node.isYieldStar,
getInferredType(judgment, inferrer), node.expression, node.fileOffset);
}
void visitLoadLibraryJudgment(
LoadLibraryJudgment node, DartType typeContext) {
node.inferredType =
inferrer.typeSchemaEnvironment.futureType(const DynamicType());
if (node.arguments != null) {
var calleeType = new FunctionType([], node.inferredType);
inferrer.inferInvocation(typeContext, node.fileOffset, calleeType,
calleeType.returnType, node.argumentJudgments);
}
return null;
}
void visitLoadLibraryTearOffJudgment(
LoadLibraryTearOffJudgment node, DartType typeContext) {
node.inferredType = new FunctionType(
[], inferrer.typeSchemaEnvironment.futureType(const DynamicType()));
return null;
}
@override
void visitCheckLibraryIsLoaded(
CheckLibraryIsLoaded node, DartType typeContext) {}
}