blob: de95a4dc9864c1e7f78665e11976f27ef9eaa73d [file] [log] [blame]
// Copyright (c) 2017, 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.
/// This file declares a "shadow hierarchy" of concrete classes which extend
/// the kernel class hierarchy, adding methods and fields needed by the
/// BodyBuilder.
///
/// Instances of these classes may be created using the factory methods in
/// `ast_factory.dart`.
///
/// Note that these classes represent the Dart language prior to desugaring.
/// When a single Dart construct desugars to a tree containing multiple kernel
/// AST nodes, the shadow class extends the kernel object at the top of the
/// desugared tree.
///
/// This means that in some cases multiple shadow classes may extend the same
/// kernel class, because multiple constructs in Dart may desugar to a tree
/// with the same kind of root node.
import 'package:front_end/src/base/instrumentation.dart';
import 'package:front_end/src/fasta/type_inference/type_inference_engine.dart';
import 'package:front_end/src/fasta/type_inference/type_inference_listener.dart';
import 'package:front_end/src/fasta/type_inference/type_inferrer.dart';
import 'package:front_end/src/fasta/type_inference/type_promotion.dart';
import 'package:front_end/src/fasta/type_inference/type_schema.dart';
import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/type_algebra.dart';
List<DartType> getExplicitTypeArguments(Arguments arguments) {
if (arguments is KernelArguments) {
return arguments._hasExplicitTypeArguments ? arguments.types : null;
} else {
// This code path should only be taken in situations where there are no
// type arguments at all, e.g. calling a user-definable operator.
assert(arguments.types.isEmpty);
return null;
}
}
/// Concrete shadow object representing a set of invocation arguments.
class KernelArguments extends Arguments {
bool _hasExplicitTypeArguments;
KernelArguments(List<Expression> positional,
{List<DartType> types, List<NamedExpression> named})
: _hasExplicitTypeArguments = types != null && types.isNotEmpty,
super(positional, types: types, named: named);
static void setExplicitArgumentTypes(
KernelArguments arguments, List<DartType> types) {
arguments.types.clear();
arguments.types.addAll(types);
arguments._hasExplicitTypeArguments = true;
}
}
/// Shadow object for [AsExpression].
class KernelAsExpression extends AsExpression implements KernelExpression {
KernelAsExpression(Expression operand, DartType type) : super(operand, type);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.asExpressionEnter(this, typeContext) || typeNeeded;
inferrer.inferExpression(operand, null, false);
var inferredType = typeNeeded ? type : null;
inferrer.listener.asExpressionExit(this, inferredType);
return inferredType;
}
}
/// Shadow object for [AwaitExpression].
class KernelAwaitExpression extends AwaitExpression
implements KernelExpression {
KernelAwaitExpression(Expression operand) : super(operand);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Concrete shadow object representing a statement block in kernel form.
class KernelBlock extends Block implements KernelStatement {
KernelBlock(List<Statement> statements) : super(statements);
@override
void _inferStatement(KernelTypeInferrer inferrer) {
inferrer.listener.blockEnter(this);
for (var statement in statements) {
inferrer.inferStatement(statement);
}
inferrer.listener.blockExit(this);
}
}
/// Concrete shadow object representing a boolean literal in kernel form.
class KernelBoolLiteral extends BoolLiteral implements KernelExpression {
KernelBoolLiteral(bool value) : super(value);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.boolLiteralEnter(this, typeContext) || typeNeeded;
var inferredType = typeNeeded ? inferrer.coreTypes.boolClass.rawType : null;
inferrer.listener.boolLiteralExit(this, inferredType);
return inferredType;
}
}
/// Concrete shadow object representing a conditional expression in kernel form.
/// Shadow object for [ConditionalExpression].
class KernelConditionalExpression extends ConditionalExpression
implements KernelExpression {
KernelConditionalExpression(
Expression condition, Expression then, Expression otherwise)
: super(condition, then, otherwise, const DynamicType());
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.conditionalExpressionEnter(this, typeContext) ||
typeNeeded;
inferrer.inferExpression(
condition, inferrer.coreTypes.boolClass.rawType, false);
// TODO(paulberry): is it correct to pass the context down?
DartType thenType = inferrer.inferExpression(then, typeContext, true);
DartType otherwiseType =
inferrer.inferExpression(otherwise, typeContext, true);
// TODO(paulberry): the spec proposal says we should only use LUB if the
// typeContext is `null`. If typeContext is non-null, we should use the
// greatest closure of the context with respect to `?`
DartType type = inferrer.typeSchemaEnvironment
.getLeastUpperBound(thenType, otherwiseType);
staticType = type;
var inferredType = typeNeeded ? type : null;
inferrer.listener.conditionalExpressionExit(this, inferredType);
return inferredType;
}
}
/// Shadow object for [ConstructorInvocation].
class KernelConstructorInvocation extends ConstructorInvocation
implements KernelExpression {
KernelConstructorInvocation(Constructor target, Arguments arguments,
{bool isConst: false})
: super(target, arguments, isConst: isConst);
KernelConstructorInvocation.byReference(
Reference targetReference, Arguments arguments)
: super.byReference(targetReference, arguments);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.constructorInvocationEnter(this, typeContext) ||
typeNeeded;
var inferredType = inferrer.inferInvocation(
typeContext,
typeNeeded,
fileOffset,
target.function.functionType,
target.enclosingClass.thisType,
arguments);
inferrer.listener.constructorInvocationExit(this, inferredType);
return inferredType;
}
}
/// Shadow object for [DirectMethodInvocation].
class KernelDirectMethodInvocation extends DirectMethodInvocation
implements KernelExpression {
KernelDirectMethodInvocation(
Expression receiver, Procedure target, Arguments arguments)
: super(receiver, target, arguments);
KernelDirectMethodInvocation.byReference(
Expression receiver, Reference targetReference, Arguments arguments)
: super.byReference(receiver, targetReference, arguments);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Shadow object for [DirectPropertyGet].
class KernelDirectPropertyGet extends DirectPropertyGet
implements KernelExpression {
KernelDirectPropertyGet(Expression receiver, Member target)
: super(receiver, target);
KernelDirectPropertyGet.byReference(
Expression receiver, Reference targetReference)
: super.byReference(receiver, targetReference);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Shadow object for [DirectPropertySet].
class KernelDirectPropertySet extends DirectPropertySet
implements KernelExpression {
KernelDirectPropertySet(Expression receiver, Member target, Expression value)
: super(receiver, target, value);
KernelDirectPropertySet.byReference(
Expression receiver, Reference targetReference, Expression value)
: super.byReference(receiver, targetReference, value);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Concrete shadow object representing a double literal in kernel form.
class KernelDoubleLiteral extends DoubleLiteral implements KernelExpression {
KernelDoubleLiteral(double value) : super(value);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.doubleLiteralEnter(this, typeContext) || typeNeeded;
var inferredType =
typeNeeded ? inferrer.coreTypes.doubleClass.rawType : null;
inferrer.listener.doubleLiteralExit(this, inferredType);
return inferredType;
}
}
/// Common base class for shadow objects representing expressions in kernel
/// form.
abstract class KernelExpression implements Expression {
/// Calls back to [inferrer] to perform type inference for whatever concrete
/// type of [KernelExpression] this is.
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded);
}
/// Concrete shadow object representing an expression statement in kernel form.
class KernelExpressionStatement extends ExpressionStatement
implements KernelStatement {
KernelExpressionStatement(Expression expression) : super(expression);
@override
void _inferStatement(KernelTypeInferrer inferrer) {
inferrer.listener.expressionStatementEnter(this);
inferrer.inferExpression(expression, null, false);
inferrer.listener.expressionStatementExit(this);
}
}
/// Shadow object for [StaticInvocation] when the procedure being invoked is a
/// factory constructor.
class KernelFactoryConstructorInvocation extends StaticInvocation
implements KernelExpression {
KernelFactoryConstructorInvocation(Procedure target, Arguments arguments,
{bool isConst: false})
: super(target, arguments, isConst: isConst);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.constructorInvocationEnter(this, typeContext) ||
typeNeeded;
var returnType = target.enclosingClass.thisType;
if (target.enclosingClass.typeParameters.isNotEmpty) {
// target.enclosingClass.typeParameters is not the same as
// target.function.functionType.typeParameters, so we have to substitute.
// TODO(paulberrry): it would be easier if we could just use
// target.function.functionType.returnType, but that's `dynamic` for
// factory constructors. Investigate whether this can be changed.
returnType = Substitution
.fromPairs(
target.enclosingClass.typeParameters,
target.function.functionType.typeParameters
.map((p) => new TypeParameterType(p))
.toList())
.substituteType(returnType);
}
var inferredType = inferrer.inferInvocation(typeContext, typeNeeded,
fileOffset, target.function.functionType, returnType, arguments);
inferrer.listener.constructorInvocationExit(this, inferredType);
return inferredType;
}
}
/// Concrete shadow object representing a field in kernel form.
class KernelField extends Field {
bool _implicitlyTyped = true;
FieldNode _fieldNode;
bool _isInferred = false;
KernelTypeInferrer _typeInferrer;
KernelField(Name name, {String fileUri}) : super(name, fileUri: fileUri) {}
@override
void set type(DartType value) {
_implicitlyTyped = false;
super.type = value;
}
String get _fileUri {
// TODO(paulberry): This is a hack. We should use this.fileUri, because we
// want the URI of the compilation unit. But that gives a relative URI,
// and I don't know what it's relative to or how to convert it to an
// absolute URI.
return enclosingLibrary.importUri.toString();
}
void _setInferredType(DartType inferredType) {
_isInferred = true;
super.type = inferredType;
}
}
/// Concrete shadow object representing a local function declaration in kernel
/// form.
class KernelFunctionDeclaration extends FunctionDeclaration
implements KernelStatement {
KernelFunctionDeclaration(VariableDeclaration variable, FunctionNode function)
: super(variable, function);
@override
void _inferStatement(KernelTypeInferrer inferrer) {
inferrer.listener.functionDeclarationEnter(this);
var oldClosureContext = inferrer.closureContext;
inferrer.closureContext =
new ClosureContext(inferrer, function.asyncMarker, function.returnType);
inferrer.inferStatement(function.body);
inferrer.closureContext = oldClosureContext;
inferrer.listener.functionDeclarationExit(this);
}
}
/// Concrete shadow object representing a function expression in kernel form.
class KernelFunctionExpression extends FunctionExpression
implements KernelExpression {
KernelFunctionExpression(FunctionNode function) : super(function);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded = inferrer.listener.functionExpressionEnter(this, typeContext) ||
typeNeeded;
// TODO(paulberry): do we also need to visit default parameter values?
// Let `<T0, ..., Tn>` be the set of type parameters of the closure (with
// `n`=0 if there are no type parameters).
List<TypeParameter> typeParameters = function.typeParameters;
// Let `(P0 x0, ..., Pm xm)` be the set of formal parameters of the closure
// (including required, positional optional, and named optional parameters).
// If any type `Pi` is missing, denote it as `_`.
List<VariableDeclaration> formals = function.positionalParameters.toList()
..addAll(function.namedParameters);
// Let `B` denote the closure body. If `B` is an expression function body
// (`=> e`), treat it as equivalent to a block function body containing a
// single `return` statement (`{ return e; }`).
// Attempt to match `K` as a function type compatible with the closure (that
// is, one having n type parameters and a compatible set of formal
// parameters). If there is a successful match, let `<S0, ..., Sn>` be the
// set of matched type parameters and `(Q0, ..., Qm)` be the set of matched
// formal parameter types, and let `N` be the return type.
Substitution substitution;
List<DartType> formalTypesFromContext =
new List<DartType>.filled(formals.length, null);
DartType returnContext;
if (inferrer.strongMode && typeContext is FunctionType) {
for (int i = 0; i < formals.length; i++) {
if (i < function.positionalParameters.length) {
formalTypesFromContext[i] =
inferrer.getPositionalParameterType(typeContext, i);
} else {
formalTypesFromContext[i] =
inferrer.getNamedParameterType(typeContext, formals[i].name);
}
}
returnContext = typeContext.returnType;
// Let `[T/S]` denote the type substitution where each `Si` is replaced with
// the corresponding `Ti`.
var substitutionMap = <TypeParameter, DartType>{};
for (int i = 0; i < typeContext.typeParameters.length; i++) {
substitutionMap[typeContext.typeParameters[i]] =
i < typeParameters.length
? new TypeParameterType(typeParameters[i])
: const DynamicType();
}
substitution = Substitution.fromMap(substitutionMap);
} else {
// If the match is not successful because `K` is `_`, let all `Si`, all
// `Qi`, and `N` all be `_`.
// If the match is not successful for any other reason, this will result in
// a type error, so the implementation is free to choose the best error
// recovery path.
substitution = Substitution.empty;
}
// Define `Ri` as follows: if `Pi` is not `_`, let `Ri` be `Pi`.
// Otherwise, if `Qi` is not `_`, let `Ri` be the greatest closure of
// `Qi[T/S]` with respect to `?`. Otherwise, let `Ri` be `dynamic`.
for (int i = 0; i < formals.length; i++) {
KernelVariableDeclaration formal = formals[i];
if (KernelVariableDeclaration.isImplicitlyTyped(formal)) {
DartType inferredType;
if (formalTypesFromContext[i] != null) {
inferredType = greatestClosure(inferrer.coreTypes,
substitution.substituteType(formalTypesFromContext[i]));
} else {
inferredType = const DynamicType();
}
inferrer.instrumentation?.record(
Uri.parse(inferrer.uri),
formal.fileOffset,
'type',
new InstrumentationValueForType(inferredType));
formal.type = inferredType;
}
}
// Let `N'` be `N[T/S]`. The [ClosureContext] constructor will adjust
// accordingly if the closure is declared with `async`, `async*`, or
// `sync*`.
if (returnContext != null) {
returnContext = substitution.substituteType(returnContext);
}
// Apply type inference to `B` in return context `N’`, with any references
// to `xi` in `B` having type `Pi`. This produces `B’`.
bool isExpressionFunction = function.body is ReturnStatement;
bool needToSetReturnType = isExpressionFunction || inferrer.strongMode;
ClosureContext oldClosureContext = inferrer.closureContext;
ClosureContext closureContext =
new ClosureContext(inferrer, function.asyncMarker, returnContext);
inferrer.closureContext = closureContext;
inferrer.inferStatement(function.body);
// If the closure is declared with `async*` or `sync*`, let `M` be the least
// upper bound of the types of the `yield` expressions in `B’`, or `void` if
// `B’` contains no `yield` expressions. Otherwise, let `M` be the least
// upper bound of the types of the `return` expressions in `B’`, or `void`
// if `B’` contains no `return` expressions.
DartType inferredReturnType;
if (needToSetReturnType || typeNeeded) {
inferredReturnType =
closureContext.inferReturnType(inferrer, isExpressionFunction);
}
// Then the result of inference is `<T0, ..., Tn>(R0 x0, ..., Rn xn) B` with
// type `<T0, ..., Tn>(R0, ..., Rn) -> M’` (with some of the `Ri` and `xi`
// denoted as optional or named parameters, if appropriate).
if (needToSetReturnType) {
inferrer.instrumentation?.record(Uri.parse(inferrer.uri), fileOffset,
'returnType', new InstrumentationValueForType(inferredReturnType));
function.returnType = inferredReturnType;
}
inferrer.closureContext = oldClosureContext;
var inferredType = typeNeeded ? function.functionType : null;
inferrer.listener.functionExpressionExit(this, inferredType);
return inferredType;
}
}
/// Concrete shadow object representing an if statement in kernel form.
class KernelIfStatement extends IfStatement implements KernelStatement {
KernelIfStatement(Expression condition, Statement then, Statement otherwise)
: super(condition, then, otherwise);
@override
void _inferStatement(KernelTypeInferrer inferrer) {
inferrer.listener.ifStatementEnter(this);
inferrer.inferExpression(
condition, inferrer.coreTypes.boolClass.rawType, false);
inferrer.inferStatement(then);
if (otherwise != null) inferrer.inferStatement(otherwise);
inferrer.listener.ifStatementExit(this);
}
}
/// Concrete shadow object representing an integer literal in kernel form.
class KernelIntLiteral extends IntLiteral implements KernelExpression {
KernelIntLiteral(int value) : super(value);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.intLiteralEnter(this, typeContext) || typeNeeded;
var inferredType = typeNeeded ? inferrer.coreTypes.intClass.rawType : null;
inferrer.listener.intLiteralExit(this, inferredType);
return inferredType;
}
}
/// Concrete shadow object representing a non-inverted "is" test in kernel form.
class KernelIsExpression extends IsExpression implements KernelExpression {
KernelIsExpression(Expression operand, DartType type) : super(operand, type);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.isExpressionEnter(this, typeContext) || typeNeeded;
inferrer.inferExpression(operand, null, false);
var inferredType = typeNeeded ? inferrer.coreTypes.boolClass.rawType : null;
inferrer.listener.isExpressionExit(this, inferredType);
return inferredType;
}
}
/// Concrete shadow object representing an inverted "is" test in kernel form.
class KernelIsNotExpression extends Not implements KernelExpression {
KernelIsNotExpression(Expression operand, DartType type, int charOffset)
: super(new IsExpression(operand, type)..fileOffset = charOffset);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
IsExpression isExpression = this.operand;
typeNeeded =
inferrer.listener.isNotExpressionEnter(this, typeContext) || typeNeeded;
inferrer.inferExpression(isExpression.operand, null, false);
var inferredType = typeNeeded ? inferrer.coreTypes.boolClass.rawType : null;
inferrer.listener.isNotExpressionExit(this, inferredType);
return inferredType;
}
}
/// Concrete shadow object representing a list literal in kernel form.
class KernelListLiteral extends ListLiteral implements KernelExpression {
final DartType _declaredTypeArgument;
KernelListLiteral(List<Expression> expressions,
{DartType typeArgument, bool isConst: false})
: _declaredTypeArgument = typeArgument,
super(expressions,
typeArgument: typeArgument ?? const DynamicType(),
isConst: isConst);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.listLiteralEnter(this, typeContext) || typeNeeded;
var listClass = inferrer.coreTypes.listClass;
var listType = listClass.thisType;
List<DartType> inferredTypes;
DartType inferredTypeArgument;
List<DartType> formalTypes;
List<DartType> actualTypes;
bool inferenceNeeded = _declaredTypeArgument == null && inferrer.strongMode;
if (inferenceNeeded) {
inferredTypes = [const UnknownType()];
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(listType,
listClass.typeParameters, null, null, typeContext, inferredTypes);
inferredTypeArgument = inferredTypes[0];
formalTypes = [];
actualTypes = [];
} else {
inferredTypeArgument = _declaredTypeArgument ?? const DynamicType();
}
for (var expression in expressions) {
var expressionType = inferrer.inferExpression(
expression, inferredTypeArgument, inferenceNeeded);
if (inferenceNeeded) {
formalTypes.add(listType.typeArguments[0]);
actualTypes.add(expressionType);
}
}
if (inferenceNeeded) {
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(
listType,
listClass.typeParameters,
formalTypes,
actualTypes,
typeContext,
inferredTypes);
inferredTypeArgument = inferredTypes[0];
inferrer.instrumentation?.record(
Uri.parse(inferrer.uri),
fileOffset,
'typeArgs',
new InstrumentationValueForTypeArgs([inferredTypeArgument]));
typeArgument = inferredTypeArgument;
}
var inferredType = typeNeeded
? new InterfaceType(listClass, [inferredTypeArgument])
: null;
inferrer.listener.listLiteralExit(this, inferredType);
return inferredType;
}
}
/// Shadow object for [LogicalExpression].
class KernelLogicalExpression extends LogicalExpression
implements KernelExpression {
KernelLogicalExpression(Expression left, String operator, Expression right)
: super(left, operator, right);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Shadow object for [MapLiteral].
class KernelMapLiteral extends MapLiteral implements KernelExpression {
KernelMapLiteral(List<MapEntry> entries,
{DartType keyType: const DynamicType(),
DartType valueType: const DynamicType(),
bool isConst: false})
: super(entries,
keyType: keyType, valueType: valueType, isConst: isConst);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Shadow object for [MethodInvocation].
class KernelMethodInvocation extends MethodInvocation
implements KernelExpression {
KernelMethodInvocation(Expression receiver, Name name, Arguments arguments,
[Procedure interfaceTarget])
: super(receiver, name, arguments, interfaceTarget);
KernelMethodInvocation.byReference(Expression receiver, Name name,
Arguments arguments, Reference interfaceTargetReference)
: super.byReference(receiver, name, arguments, interfaceTargetReference);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded = inferrer.listener.methodInvocationEnter(this, typeContext) ||
typeNeeded;
// First infer the receiver so we can look up the method that was invoked.
var receiverType = inferrer.inferExpression(receiver, null, true);
bool isOverloadedArithmeticOperator = false;
Member interfaceMember;
if (receiverType is InterfaceType) {
interfaceMember = inferrer.classHierarchy
.getInterfaceMember(receiverType.classNode, name);
if (interfaceMember is Procedure) {
// Our non-strong golden files currently don't include interface
// targets, so we can't store the interface target without causing tests
// to fail. TODO(paulberry): fix this.
if (inferrer.strongMode) {
interfaceTarget = interfaceMember;
}
isOverloadedArithmeticOperator = inferrer.typeSchemaEnvironment
.isOverloadedArithmeticOperator(interfaceMember);
}
}
var calleeType = inferrer.getCalleeFunctionType(
interfaceMember, receiverType, name, fileOffset);
var inferredType = inferrer.inferInvocation(typeContext, typeNeeded,
fileOffset, calleeType, calleeType.returnType, arguments,
isOverloadedArithmeticOperator: isOverloadedArithmeticOperator,
receiverType: receiverType);
inferrer.listener.methodInvocationExit(this, inferredType);
return inferredType;
}
}
/// Shadow object for [Not].
class KernelNot extends Not implements KernelExpression {
KernelNot(Expression operand) : super(operand);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Concrete shadow object representing a null literal in kernel form.
class KernelNullLiteral extends NullLiteral implements KernelExpression {
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.nullLiteralEnter(this, typeContext) || typeNeeded;
var inferredType = typeNeeded ? inferrer.coreTypes.nullClass.rawType : null;
inferrer.listener.nullLiteralExit(this, inferredType);
return inferredType;
}
}
/// Shadow object for [PropertyGet].
class KernelPropertyGet extends PropertyGet implements KernelExpression {
KernelPropertyGet(Expression receiver, Name name, [Member interfaceTarget])
: super(receiver, name, interfaceTarget);
KernelPropertyGet.byReference(
Expression receiver, Name name, Reference interfaceTargetReference)
: super.byReference(receiver, name, interfaceTargetReference);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Shadow object for [PropertyGet].
class KernelPropertySet extends PropertySet implements KernelExpression {
KernelPropertySet(Expression receiver, Name name, Expression value,
[Member interfaceTarget])
: super(receiver, name, value, interfaceTarget);
KernelPropertySet.byReference(Expression receiver, Name name,
Expression value, Reference interfaceTargetReference)
: super.byReference(receiver, name, value, interfaceTargetReference);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Shadow object for [Rethrow].
class KernelRethrow extends Rethrow implements KernelExpression {
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Concrete shadow object representing a return statement in kernel form.
class KernelReturnStatement extends ReturnStatement implements KernelStatement {
KernelReturnStatement([Expression expression]) : super(expression);
@override
void _inferStatement(KernelTypeInferrer inferrer) {
inferrer.listener.returnStatementEnter(this);
var closureContext = inferrer.closureContext;
var typeContext =
!closureContext.isGenerator ? closureContext.returnContext : null;
var inferredType = expression != null
? inferrer.inferExpression(expression, typeContext, true)
: const VoidType();
// Analyzer treats bare `return` statements as having no effect on the
// inferred type of the closure. TODO(paulberry): is this what we want
// for Fasta?
if (expression != null) {
closureContext.handleReturn(inferrer, inferredType);
}
inferrer.listener.returnStatementExit(this);
}
}
/// Common base class for shadow objects representing statements in kernel
/// form.
abstract class KernelStatement extends Statement {
/// Calls back to [inferrer] to perform type inference for whatever concrete
/// type of [KernelStatement] this is.
void _inferStatement(KernelTypeInferrer inferrer);
}
/// Concrete shadow object representing a read of a static variable in kernel
/// form.
class KernelStaticGet extends StaticGet implements KernelExpression {
KernelStaticGet(Member target) : super(target);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.staticGetEnter(this, typeContext) || typeNeeded;
var inferredType = typeNeeded ? target.getterType : null;
inferrer.listener.staticGetExit(this, inferredType);
return inferredType;
}
}
/// Shadow object for [StaticInvocation].
class KernelStaticInvocation extends StaticInvocation
implements KernelExpression {
KernelStaticInvocation(Procedure target, Arguments arguments,
{bool isConst: false})
: super(target, arguments, isConst: isConst);
KernelStaticInvocation.byReference(
Reference targetReference, Arguments arguments)
: super.byReference(targetReference, arguments);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded = inferrer.listener.staticInvocationEnter(this, typeContext) ||
typeNeeded;
var calleeType = target.function.functionType;
var inferredType = inferrer.inferInvocation(typeContext, typeNeeded,
fileOffset, calleeType, calleeType.returnType, arguments);
inferrer.listener.staticInvocationExit(this, inferredType);
return inferredType;
}
}
/// Shadow object for [StaticSet].
class KernelStaticSet extends StaticSet implements KernelExpression {
KernelStaticSet(Member target, Expression value) : super(target, value);
KernelStaticSet.byReference(Reference targetReference, Expression value)
: super.byReference(targetReference, value);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Concrete shadow object representing a string concatenation in kernel form.
class KernelStringConcatenation extends StringConcatenation
implements KernelExpression {
KernelStringConcatenation(List<Expression> expressions) : super(expressions);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.stringConcatenationEnter(this, typeContext) ||
typeNeeded;
for (Expression expression in expressions) {
inferrer.inferExpression(expression, null, false);
}
var inferredType =
typeNeeded ? inferrer.coreTypes.stringClass.rawType : null;
inferrer.listener.stringConcatenationExit(this, inferredType);
return inferredType;
}
}
/// Concrete shadow object representing a string literal in kernel form.
class KernelStringLiteral extends StringLiteral implements KernelExpression {
KernelStringLiteral(String value) : super(value);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.stringLiteralEnter(this, typeContext) || typeNeeded;
var inferredType =
typeNeeded ? inferrer.coreTypes.stringClass.rawType : null;
inferrer.listener.stringLiteralExit(this, inferredType);
return inferredType;
}
}
/// Shadow object for [SuperMethodInvocation].
class KernelSuperMethodInvocation extends SuperMethodInvocation
implements KernelExpression {
KernelSuperMethodInvocation(Name name, Arguments arguments,
[Procedure interfaceTarget])
: super(name, arguments, interfaceTarget);
KernelSuperMethodInvocation.byReference(
Name name, Arguments arguments, Reference interfaceTargetReference)
: super.byReference(name, arguments, interfaceTargetReference);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Shadow object for [SuperPropertyGet].
class KernelSuperPropertyGet extends SuperPropertyGet
implements KernelExpression {
KernelSuperPropertyGet(Name name, [Member interfaceTarget])
: super(name, interfaceTarget);
KernelSuperPropertyGet.byReference(
Name name, Reference interfaceTargetReference)
: super.byReference(name, interfaceTargetReference);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Shadow object for [SuperPropertySet].
class KernelSuperPropertySet extends SuperPropertySet
implements KernelExpression {
KernelSuperPropertySet(Name name, Expression value, Member interfaceTarget)
: super(name, value, interfaceTarget);
KernelSuperPropertySet.byReference(
Name name, Expression value, Reference interfaceTargetReference)
: super.byReference(name, value, interfaceTargetReference);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Shadow object for [SymbolLiteral].
class KernelSymbolLiteral extends SymbolLiteral implements KernelExpression {
KernelSymbolLiteral(String value) : super(value);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Shadow object for [ThisExpression].
class KernelThisExpression extends ThisExpression implements KernelExpression {
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Shadow object for [Throw].
class KernelThrow extends Throw implements KernelExpression {
KernelThrow(Expression expression) : super(expression);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
inferrer.inferExpression(expression, null, false);
return typeNeeded ? const BottomType() : null;
}
}
/// Concrete implementation of [TypeInferenceEngine] specialized to work with
/// kernel objects.
class KernelTypeInferenceEngine extends TypeInferenceEngineImpl {
KernelTypeInferenceEngine(Instrumentation instrumentation, bool strongMode)
: super(instrumentation, strongMode);
@override
void clearFieldInitializer(KernelField field) {
field.initializer = null;
}
@override
FieldNode createFieldNode(KernelField field) {
FieldNode fieldNode = new FieldNode(this, field);
field._fieldNode = fieldNode;
return fieldNode;
}
@override
KernelTypeInferrer createLocalTypeInferrer(
Uri uri, TypeInferenceListener listener) {
return new KernelTypeInferrer._(this, uri.toString(), listener);
}
@override
KernelTypeInferrer createTopLevelTypeInferrer(
KernelField field, TypeInferenceListener listener) {
return field._typeInferrer =
new KernelTypeInferrer._(this, getFieldUri(field), listener);
}
@override
bool fieldHasInitializer(KernelField field) {
return field.initializer != null;
}
@override
DartType getFieldDeclaredType(KernelField field) {
return field._implicitlyTyped ? null : field.type;
}
@override
List<FieldNode> getFieldDependencies(KernelField field) {
return field._fieldNode?.dependencies;
}
@override
int getFieldOffset(KernelField field) {
return field.fileOffset;
}
@override
KernelTypeInferrer getFieldTypeInferrer(KernelField field) {
return field._typeInferrer;
}
@override
String getFieldUri(KernelField field) {
return field._fileUri;
}
@override
bool isFieldInferred(KernelField field) {
return field._isInferred;
}
@override
void setFieldInferredType(KernelField field, DartType inferredType) {
field._setInferredType(inferredType);
}
}
/// Concrete implementation of [TypeInferrer] specialized to work with kernel
/// objects.
class KernelTypeInferrer extends TypeInferrerImpl {
@override
final typePromoter = new KernelTypePromoter();
KernelTypeInferrer._(KernelTypeInferenceEngine engine, String uri,
TypeInferenceListener listener)
: super(engine, uri, listener);
@override
Expression getFieldInitializer(KernelField field) {
return field.initializer;
}
@override
FieldNode getFieldNodeForReadTarget(Member readTarget) {
if (readTarget is KernelField) {
return readTarget._fieldNode;
} else {
return null;
}
}
@override
DartType inferExpression(
Expression expression, DartType typeContext, bool typeNeeded) {
if (expression is KernelExpression) {
// Use polymorphic dispatch on [KernelExpression] to perform whatever kind
// of type inference is correct for this kind of statement.
// TODO(paulberry): experiment to see if dynamic dispatch would be better,
// so that the type hierarchy will be simpler (which may speed up "is"
// checks).
return expression._inferExpression(this, typeContext, typeNeeded);
} else {
// Encountered an expression type for which type inference is not yet
// implemented, so just infer dynamic for now.
// TODO(paulberry): once the BodyBuilder uses shadow classes for
// everything, this case should no longer be needed.
return typeNeeded ? const DynamicType() : null;
}
}
@override
DartType inferFieldInitializer(
KernelField field, DartType type, bool typeNeeded) {
return inferExpression(field.initializer, type, typeNeeded);
}
@override
void inferStatement(Statement statement) {
if (statement is KernelStatement) {
// Use polymorphic dispatch on [KernelStatement] to perform whatever kind
// of type inference is correct for this kind of statement.
// TODO(paulberry): experiment to see if dynamic dispatch would be better,
// so that the type hierarchy will be simpler (which may speed up "is"
// checks).
return statement._inferStatement(this);
} else {
// Encountered a statement type for which type inference is not yet
// implemented, so just skip it for now.
// TODO(paulberry): once the BodyBuilder uses shadow classes for
// everything, this case should no longer be needed.
}
}
}
/// Shadow object for [TypeLiteral].
class KernelTypeLiteral extends TypeLiteral implements KernelExpression {
KernelTypeLiteral(DartType type) : super(type);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
}
}
/// Concrete implementation of [TypePromoter] specialized to work with kernel
/// objects.
///
/// Note: the second type parameter really ought to be
/// KernelVariableDeclaration, but we can't do that yet because BodyBuilder
/// still uses raw VariableDeclaration objects sometimes.
/// TODO(paulberry): fix this.
class KernelTypePromoter
extends TypePromoterImpl<Expression, VariableDeclaration> {
@override
int getVariableFunctionNestingLevel(VariableDeclaration variable) {
if (variable is KernelVariableDeclaration) {
return variable._functionNestingLevel;
} else {
// Hack to deal with the fact that BodyBuilder still creates raw
// VariableDeclaration objects sometimes.
// TODO(paulberry): get rid of this once the type parameter is
// KernelVariableDeclaration.
return 0;
}
}
@override
bool isPromotionCandidate(VariableDeclaration variable) {
if (variable is KernelVariableDeclaration) {
return !variable._isLocalFunction;
} else {
// Hack to deal with the fact that BodyBuilder still creates raw
// VariableDeclaration objects sometimes.
// TODO(paulberry): get rid of this once the type parameter is
// KernelVariableDeclaration.
return true;
}
}
@override
bool sameExpressions(Expression a, Expression b) {
return identical(a, b);
}
@override
void setVariableMutatedAnywhere(VariableDeclaration variable) {
if (variable is KernelVariableDeclaration) {
variable._mutatedAnywhere = true;
} else {
// Hack to deal with the fact that BodyBuilder still creates raw
// VariableDeclaration objects sometimes.
// TODO(paulberry): get rid of this once the type parameter is
// KernelVariableDeclaration.
}
}
@override
void setVariableMutatedInClosure(VariableDeclaration variable) {
if (variable is KernelVariableDeclaration) {
variable._mutatedInClosure = true;
} else {
// Hack to deal with the fact that BodyBuilder still creates raw
// VariableDeclaration objects sometimes.
// TODO(paulberry): get rid of this once the type parameter is
// KernelVariableDeclaration.
}
}
@override
bool wasVariableMutatedAnywhere(VariableDeclaration variable) {
if (variable is KernelVariableDeclaration) {
return variable._mutatedAnywhere;
} else {
// Hack to deal with the fact that BodyBuilder still creates raw
// VariableDeclaration objects sometimes.
// TODO(paulberry): get rid of this once the type parameter is
// KernelVariableDeclaration.
return true;
}
}
}
/// Concrete shadow object representing a variable declaration in kernel form.
class KernelVariableDeclaration extends VariableDeclaration
implements KernelStatement {
final bool _implicitlyTyped;
final int _functionNestingLevel;
bool _mutatedInClosure = false;
bool _mutatedAnywhere = false;
final bool _isLocalFunction;
KernelVariableDeclaration(String name, this._functionNestingLevel,
{Expression initializer,
DartType type,
bool isFinal: false,
bool isConst: false,
bool isLocalFunction: false})
: _implicitlyTyped = type == null,
_isLocalFunction = isLocalFunction,
super(name,
initializer: initializer,
type: type ?? const DynamicType(),
isFinal: isFinal,
isConst: isConst);
@override
void _inferStatement(KernelTypeInferrer inferrer) {
inferrer.listener.variableDeclarationEnter(this);
var declaredType = _implicitlyTyped ? null : type;
if (initializer != null) {
var inferredType = inferrer.inferDeclarationType(inferrer.inferExpression(
initializer, declaredType, _implicitlyTyped));
if (inferrer.strongMode && _implicitlyTyped) {
inferrer.instrumentation?.record(Uri.parse(inferrer.uri), fileOffset,
'type', new InstrumentationValueForType(inferredType));
type = inferredType;
}
}
inferrer.listener.variableDeclarationExit(this);
}
/// Determine whether the given [KernelVariableDeclaration] had an implicit
/// type.
///
/// This is static to avoid introducing a method that would be visible to
/// the kernel.
static bool isImplicitlyTyped(KernelVariableDeclaration variable) =>
variable._implicitlyTyped;
}
/// Concrete shadow object representing a read from a variable in kernel form.
class KernelVariableGet extends VariableGet implements KernelExpression {
final TypePromotionFact<VariableDeclaration> _fact;
final TypePromotionScope _scope;
KernelVariableGet(VariableDeclaration variable, this._fact, this._scope)
: super(variable);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
var variable = this.variable as KernelVariableDeclaration;
bool mutatedInClosure = variable._mutatedInClosure;
DartType declaredOrInferredType = variable.type;
typeNeeded =
inferrer.listener.variableGetEnter(this, typeContext) || typeNeeded;
DartType promotedType = inferrer.typePromoter
.computePromotedType(_fact, _scope, mutatedInClosure);
if (promotedType != null) {
inferrer.instrumentation?.record(Uri.parse(inferrer.uri), fileOffset,
'promotedType', new InstrumentationValueForType(promotedType));
}
this.promotedType = promotedType;
var inferredType =
typeNeeded ? (promotedType ?? declaredOrInferredType) : null;
inferrer.listener.variableGetExit(this, inferredType);
return inferredType;
}
}
/// Concrete shadow object representing a write to a variable in kernel form.
class KernelVariableSet extends VariableSet implements KernelExpression {
KernelVariableSet(VariableDeclaration variable, Expression value)
: super(variable, value);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
var variable = this.variable as KernelVariableDeclaration;
typeNeeded =
inferrer.listener.variableSetEnter(this, typeContext) || typeNeeded;
var inferredType =
inferrer.inferExpression(value, variable.type, typeNeeded);
inferrer.listener.variableSetExit(this, inferredType);
return inferredType;
}
}
/// Concrete shadow object representing a yield statement in kernel form.
class KernelYieldStatement extends YieldStatement implements KernelStatement {
KernelYieldStatement(Expression expression, {bool isYieldStar: false})
: super(expression, isYieldStar: isYieldStar);
@override
void _inferStatement(KernelTypeInferrer inferrer) {
inferrer.listener.yieldStatementEnter(this);
var closureContext = inferrer.closureContext;
var typeContext =
closureContext.isGenerator ? closureContext.returnContext : null;
if (isYieldStar && typeContext != null) {
typeContext = inferrer.wrapType(
typeContext,
closureContext.isAsync
? inferrer.coreTypes.streamClass
: inferrer.coreTypes.iterableClass);
}
var inferredType = inferrer.inferExpression(
expression, typeContext, closureContext != null);
closureContext.handleYield(inferrer, isYieldStar, inferredType);
inferrer.listener.yieldStatementExit(this);
}
}