2016-11-01 14:43:31 +00:00
|
|
|
// Copyright (c) 2016, 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.
|
|
|
|
library kernel.type_checker;
|
|
|
|
|
|
|
|
import 'ast.dart';
|
|
|
|
import 'class_hierarchy.dart';
|
|
|
|
import 'core_types.dart';
|
|
|
|
import 'type_algebra.dart';
|
|
|
|
import 'type_environment.dart';
|
|
|
|
|
|
|
|
/// Performs strong-mode type checking on the kernel IR.
|
|
|
|
///
|
|
|
|
/// A concrete subclass of [TypeChecker] must implement [checkAssignable] and
|
|
|
|
/// [fail] in order to deal with subtyping requirements and error handling.
|
|
|
|
abstract class TypeChecker {
|
|
|
|
final CoreTypes coreTypes;
|
|
|
|
final ClassHierarchy hierarchy;
|
2017-10-10 06:42:02 +00:00
|
|
|
final bool ignoreSdk;
|
2016-11-01 14:43:31 +00:00
|
|
|
TypeEnvironment environment;
|
|
|
|
|
2017-10-10 06:42:02 +00:00
|
|
|
TypeChecker(this.coreTypes, this.hierarchy,
|
|
|
|
{bool strongMode: false, this.ignoreSdk: true}) {
|
|
|
|
environment =
|
|
|
|
new TypeEnvironment(coreTypes, hierarchy, strongMode: strongMode);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
|
2018-03-15 12:22:23 +00:00
|
|
|
void checkComponent(Component component) {
|
|
|
|
for (var library in component.libraries) {
|
2017-10-10 06:42:02 +00:00
|
|
|
if (ignoreSdk && library.importUri.scheme == 'dart') continue;
|
2016-11-01 14:43:31 +00:00
|
|
|
for (var class_ in library.classes) {
|
|
|
|
hierarchy.forEachOverridePair(class_,
|
|
|
|
(Member ownMember, Member superMember, bool isSetter) {
|
|
|
|
checkOverride(class_, ownMember, superMember, isSetter);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var visitor = new TypeCheckingVisitor(this, environment);
|
2018-03-15 12:22:23 +00:00
|
|
|
for (var library in component.libraries) {
|
2017-10-10 06:42:02 +00:00
|
|
|
if (ignoreSdk && library.importUri.scheme == 'dart') continue;
|
2016-11-01 14:43:31 +00:00
|
|
|
for (var class_ in library.classes) {
|
|
|
|
environment.thisType = class_.thisType;
|
|
|
|
for (var field in class_.fields) {
|
|
|
|
visitor.visitField(field);
|
|
|
|
}
|
|
|
|
for (var constructor in class_.constructors) {
|
|
|
|
visitor.visitConstructor(constructor);
|
|
|
|
}
|
|
|
|
for (var procedure in class_.procedures) {
|
|
|
|
visitor.visitProcedure(procedure);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
environment.thisType = null;
|
|
|
|
for (var procedure in library.procedures) {
|
|
|
|
visitor.visitProcedure(procedure);
|
|
|
|
}
|
|
|
|
for (var field in library.fields) {
|
|
|
|
visitor.visitField(field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DartType getterType(Class host, Member member) {
|
|
|
|
var hostType = hierarchy.getClassAsInstanceOf(host, member.enclosingClass);
|
|
|
|
var substitution = Substitution.fromSupertype(hostType);
|
|
|
|
return substitution.substituteType(member.getterType);
|
|
|
|
}
|
|
|
|
|
|
|
|
DartType setterType(Class host, Member member) {
|
|
|
|
var hostType = hierarchy.getClassAsInstanceOf(host, member.enclosingClass);
|
|
|
|
var substitution = Substitution.fromSupertype(hostType);
|
|
|
|
return substitution.substituteType(member.setterType, contravariant: true);
|
|
|
|
}
|
|
|
|
|
2017-10-10 06:42:02 +00:00
|
|
|
/// Check that [ownMember] of [host] can override [superMember].
|
2016-11-01 14:43:31 +00:00
|
|
|
void checkOverride(
|
|
|
|
Class host, Member ownMember, Member superMember, bool isSetter) {
|
|
|
|
if (isSetter) {
|
|
|
|
checkAssignable(ownMember, setterType(host, superMember),
|
|
|
|
setterType(host, ownMember));
|
|
|
|
} else {
|
|
|
|
checkAssignable(ownMember, getterType(host, ownMember),
|
|
|
|
getterType(host, superMember));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check that [from] is a subtype of [to].
|
|
|
|
///
|
|
|
|
/// [where] is an AST node indicating roughly where the check is required.
|
|
|
|
void checkAssignable(TreeNode where, DartType from, DartType to);
|
|
|
|
|
2017-01-11 12:47:40 +00:00
|
|
|
/// Checks that [expression], which has type [from], can be assigned to [to].
|
|
|
|
///
|
|
|
|
/// Should return a downcast if necessary, or [expression] if no cast is
|
|
|
|
/// needed.
|
|
|
|
Expression checkAndDowncastExpression(
|
|
|
|
Expression expression, DartType from, DartType to) {
|
|
|
|
checkAssignable(expression, from, to);
|
|
|
|
return expression;
|
|
|
|
}
|
|
|
|
|
2017-10-11 06:35:35 +00:00
|
|
|
/// Check unresolved invocation (one that has no interfaceTarget)
|
|
|
|
/// and report an error if necessary.
|
|
|
|
void checkUnresolvedInvocation(DartType receiver, TreeNode where) {
|
|
|
|
// By default we ignore unresolved method invocations.
|
|
|
|
}
|
|
|
|
|
2016-11-01 14:43:31 +00:00
|
|
|
/// Indicates that type checking failed.
|
|
|
|
void fail(TreeNode where, String message);
|
|
|
|
}
|
|
|
|
|
|
|
|
class TypeCheckingVisitor
|
|
|
|
implements
|
|
|
|
ExpressionVisitor<DartType>,
|
|
|
|
StatementVisitor<Null>,
|
|
|
|
MemberVisitor<Null>,
|
|
|
|
InitializerVisitor<Null> {
|
|
|
|
final TypeChecker checker;
|
|
|
|
final TypeEnvironment environment;
|
|
|
|
|
|
|
|
CoreTypes get coreTypes => environment.coreTypes;
|
|
|
|
ClassHierarchy get hierarchy => environment.hierarchy;
|
|
|
|
Class get currentClass => environment.thisType.classNode;
|
|
|
|
|
|
|
|
TypeCheckingVisitor(this.checker, this.environment);
|
|
|
|
|
|
|
|
void checkAssignable(TreeNode where, DartType from, DartType to) {
|
|
|
|
checker.checkAssignable(where, from, to);
|
|
|
|
}
|
|
|
|
|
2017-10-11 06:35:35 +00:00
|
|
|
void checkUnresolvedInvocation(DartType receiver, TreeNode where) {
|
|
|
|
checker.checkUnresolvedInvocation(receiver, where);
|
|
|
|
}
|
|
|
|
|
2017-01-11 12:47:40 +00:00
|
|
|
Expression checkAndDowncastExpression(Expression from, DartType to) {
|
|
|
|
var parent = from.parent;
|
|
|
|
var type = visitExpression(from);
|
|
|
|
var result = checker.checkAndDowncastExpression(from, type, to);
|
|
|
|
result.parent = parent;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void checkExpressionNoDowncast(Expression expression, DartType to) {
|
|
|
|
checkAssignable(expression, visitExpression(expression), to);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void fail(TreeNode node, String message) {
|
|
|
|
checker.fail(node, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
DartType visitExpression(Expression node) => node.accept(this);
|
|
|
|
|
|
|
|
void visitStatement(Statement node) {
|
|
|
|
node.accept(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visitInitializer(Initializer node) {
|
|
|
|
node.accept(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultMember(Member node) => throw 'Unused';
|
|
|
|
|
|
|
|
DartType defaultBasicLiteral(BasicLiteral node) {
|
|
|
|
return defaultExpression(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
DartType defaultExpression(Expression node) {
|
|
|
|
throw 'Unexpected expression ${node.runtimeType}';
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultStatement(Statement node) {
|
|
|
|
throw 'Unexpected statement ${node.runtimeType}';
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultInitializer(Initializer node) {
|
|
|
|
throw 'Unexpected initializer ${node.runtimeType}';
|
|
|
|
}
|
|
|
|
|
|
|
|
visitField(Field node) {
|
|
|
|
if (node.initializer != null) {
|
2017-01-11 12:47:40 +00:00
|
|
|
node.initializer =
|
|
|
|
checkAndDowncastExpression(node.initializer, node.type);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
visitConstructor(Constructor node) {
|
|
|
|
environment.returnType = null;
|
|
|
|
environment.yieldType = null;
|
|
|
|
node.initializers.forEach(visitInitializer);
|
|
|
|
handleFunctionNode(node.function);
|
|
|
|
}
|
|
|
|
|
|
|
|
visitProcedure(Procedure node) {
|
2016-11-03 14:08:57 +00:00
|
|
|
environment.returnType = _getInternalReturnType(node.function);
|
2016-11-01 14:43:31 +00:00
|
|
|
environment.yieldType = _getYieldType(node.function);
|
|
|
|
handleFunctionNode(node.function);
|
|
|
|
}
|
|
|
|
|
2017-11-28 17:36:44 +00:00
|
|
|
visitRedirectingFactoryConstructor(RedirectingFactoryConstructor node) {
|
|
|
|
environment.returnType = null;
|
|
|
|
environment.yieldType = null;
|
|
|
|
}
|
|
|
|
|
2016-11-01 14:43:31 +00:00
|
|
|
void handleFunctionNode(FunctionNode node) {
|
2016-11-03 14:08:57 +00:00
|
|
|
var oldAsyncMarker = environment.currentAsyncMarker;
|
|
|
|
environment.currentAsyncMarker = node.asyncMarker;
|
2016-11-01 14:43:31 +00:00
|
|
|
node.positionalParameters
|
|
|
|
.skip(node.requiredParameterCount)
|
|
|
|
.forEach(handleOptionalParameter);
|
|
|
|
node.namedParameters.forEach(handleOptionalParameter);
|
|
|
|
if (node.body != null) {
|
|
|
|
visitStatement(node.body);
|
|
|
|
}
|
2016-11-03 14:08:57 +00:00
|
|
|
environment.currentAsyncMarker = oldAsyncMarker;
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void handleNestedFunctionNode(FunctionNode node) {
|
|
|
|
var oldReturn = environment.returnType;
|
|
|
|
var oldYield = environment.yieldType;
|
2016-11-03 14:08:57 +00:00
|
|
|
environment.returnType = _getInternalReturnType(node);
|
2016-11-01 14:43:31 +00:00
|
|
|
environment.yieldType = _getYieldType(node);
|
|
|
|
handleFunctionNode(node);
|
|
|
|
environment.returnType = oldReturn;
|
|
|
|
environment.yieldType = oldYield;
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleOptionalParameter(VariableDeclaration parameter) {
|
|
|
|
if (parameter.initializer != null) {
|
2017-01-11 12:47:40 +00:00
|
|
|
// Default parameter values cannot be downcast.
|
|
|
|
checkExpressionNoDowncast(parameter.initializer, parameter.type);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Substitution getReceiverType(
|
|
|
|
TreeNode access, Expression receiver, Member member) {
|
|
|
|
var type = visitExpression(receiver);
|
|
|
|
Class superclass = member.enclosingClass;
|
|
|
|
if (superclass.supertype == null) {
|
|
|
|
return Substitution.empty; // Members on Object are always accessible.
|
|
|
|
}
|
|
|
|
while (type is TypeParameterType) {
|
2017-10-19 14:24:27 +00:00
|
|
|
type = (type as TypeParameterType).bound;
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
if (type is BottomType) {
|
|
|
|
// The bottom type is a subtype of all types, so it should be allowed.
|
|
|
|
return Substitution.bottomForClass(superclass);
|
|
|
|
}
|
|
|
|
if (type is InterfaceType) {
|
|
|
|
// The receiver type should implement the interface declaring the member.
|
|
|
|
var upcastType = hierarchy.getTypeAsInstanceOf(type, superclass);
|
|
|
|
if (upcastType != null) {
|
|
|
|
return Substitution.fromInterfaceType(upcastType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (type is FunctionType && superclass == coreTypes.functionClass) {
|
|
|
|
assert(type.typeParameters.isEmpty);
|
|
|
|
return Substitution.empty;
|
|
|
|
}
|
|
|
|
// Note that we do not allow 'dynamic' here. Dynamic calls should not
|
|
|
|
// have a declared interface target.
|
|
|
|
fail(access, '$member is not accessible on a receiver of type $type');
|
|
|
|
return Substitution.bottomForClass(superclass); // Continue type checking.
|
|
|
|
}
|
|
|
|
|
|
|
|
Substitution getSuperReceiverType(Member member) {
|
|
|
|
return Substitution.fromSupertype(
|
|
|
|
hierarchy.getClassAsInstanceOf(currentClass, member.enclosingClass));
|
|
|
|
}
|
|
|
|
|
2017-06-07 16:20:50 +00:00
|
|
|
DartType handleCall(Arguments arguments, DartType functionType,
|
2016-11-01 14:43:31 +00:00
|
|
|
{Substitution receiver: Substitution.empty,
|
|
|
|
List<TypeParameter> typeParameters}) {
|
2017-06-07 16:20:50 +00:00
|
|
|
if (functionType is FunctionType) {
|
|
|
|
typeParameters ??= functionType.typeParameters;
|
|
|
|
if (arguments.positional.length < functionType.requiredParameterCount) {
|
|
|
|
fail(arguments, 'Too few positional arguments');
|
|
|
|
return const BottomType();
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
2017-06-07 16:20:50 +00:00
|
|
|
if (arguments.positional.length >
|
|
|
|
functionType.positionalParameters.length) {
|
|
|
|
fail(arguments, 'Too many positional arguments');
|
2016-11-01 14:43:31 +00:00
|
|
|
return const BottomType();
|
|
|
|
}
|
2017-12-15 20:41:43 +00:00
|
|
|
var typeArguments = arguments.types;
|
|
|
|
if (typeArguments.length != typeParameters.length) {
|
2017-06-07 16:20:50 +00:00
|
|
|
fail(arguments, 'Wrong number of type arguments');
|
|
|
|
return const BottomType();
|
|
|
|
}
|
2017-12-15 20:41:43 +00:00
|
|
|
Substitution substitution = _instantiateFunction(
|
|
|
|
typeParameters, typeArguments, arguments,
|
|
|
|
receiverSubstitution: receiver);
|
2017-06-07 16:20:50 +00:00
|
|
|
for (int i = 0; i < arguments.positional.length; ++i) {
|
|
|
|
var expectedType = substitution.substituteType(
|
|
|
|
functionType.positionalParameters[i],
|
|
|
|
contravariant: true);
|
|
|
|
arguments.positional[i] =
|
|
|
|
checkAndDowncastExpression(arguments.positional[i], expectedType);
|
|
|
|
}
|
|
|
|
for (int i = 0; i < arguments.named.length; ++i) {
|
|
|
|
var argument = arguments.named[i];
|
|
|
|
bool found = false;
|
|
|
|
for (int j = 0; j < functionType.namedParameters.length; ++j) {
|
|
|
|
if (argument.name == functionType.namedParameters[j].name) {
|
|
|
|
var expectedType = substitution.substituteType(
|
|
|
|
functionType.namedParameters[j].type,
|
|
|
|
contravariant: true);
|
|
|
|
argument.value =
|
|
|
|
checkAndDowncastExpression(argument.value, expectedType);
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
fail(argument.value, 'Unexpected named parameter: ${argument.name}');
|
|
|
|
return const BottomType();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return substitution.substituteType(functionType.returnType);
|
|
|
|
} else {
|
|
|
|
// Note: attempting to resolve .call() on [functionType] could lead to an
|
|
|
|
// infinite regress, so just assume `dynamic`.
|
|
|
|
return const DynamicType();
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-03 14:08:57 +00:00
|
|
|
DartType _getInternalReturnType(FunctionNode function) {
|
|
|
|
switch (function.asyncMarker) {
|
|
|
|
case AsyncMarker.Sync:
|
|
|
|
return function.returnType;
|
|
|
|
|
|
|
|
case AsyncMarker.Async:
|
|
|
|
Class container = coreTypes.futureClass;
|
|
|
|
DartType returnType = function.returnType;
|
|
|
|
if (returnType is InterfaceType && returnType.classNode == container) {
|
|
|
|
return returnType.typeArguments.single;
|
|
|
|
}
|
|
|
|
return const DynamicType();
|
|
|
|
|
|
|
|
case AsyncMarker.SyncStar:
|
|
|
|
case AsyncMarker.AsyncStar:
|
2017-10-23 14:40:57 +00:00
|
|
|
return null;
|
|
|
|
|
2016-11-03 14:08:57 +00:00
|
|
|
case AsyncMarker.SyncYielding:
|
2017-10-23 14:40:57 +00:00
|
|
|
TreeNode parent = function.parent;
|
|
|
|
while (parent is! FunctionNode) {
|
|
|
|
parent = parent.parent;
|
|
|
|
}
|
|
|
|
final enclosingFunction = parent as FunctionNode;
|
|
|
|
if (enclosingFunction.dartAsyncMarker == AsyncMarker.SyncStar) {
|
|
|
|
return coreTypes.boolClass.rawType;
|
|
|
|
}
|
2016-11-03 14:08:57 +00:00
|
|
|
return null;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw 'Unexpected async marker: ${function.asyncMarker}';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-01 14:43:31 +00:00
|
|
|
DartType _getYieldType(FunctionNode function) {
|
|
|
|
switch (function.asyncMarker) {
|
|
|
|
case AsyncMarker.Sync:
|
|
|
|
case AsyncMarker.Async:
|
|
|
|
return null;
|
|
|
|
|
|
|
|
case AsyncMarker.SyncStar:
|
|
|
|
case AsyncMarker.AsyncStar:
|
|
|
|
Class container = function.asyncMarker == AsyncMarker.SyncStar
|
|
|
|
? coreTypes.iterableClass
|
|
|
|
: coreTypes.streamClass;
|
|
|
|
DartType returnType = function.returnType;
|
|
|
|
if (returnType is InterfaceType && returnType.classNode == container) {
|
|
|
|
return returnType.typeArguments.single;
|
|
|
|
}
|
|
|
|
return const DynamicType();
|
|
|
|
|
|
|
|
case AsyncMarker.SyncYielding:
|
|
|
|
return function.returnType;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw 'Unexpected async marker: ${function.asyncMarker}';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-15 20:41:43 +00:00
|
|
|
Substitution _instantiateFunction(List<TypeParameter> typeParameters,
|
|
|
|
List<DartType> typeArguments, TreeNode where,
|
|
|
|
{Substitution receiverSubstitution}) {
|
|
|
|
var instantiation = Substitution.fromPairs(typeParameters, typeArguments);
|
|
|
|
var substitution = receiverSubstitution == null
|
|
|
|
? instantiation
|
|
|
|
: Substitution.combine(receiverSubstitution, instantiation);
|
|
|
|
for (int i = 0; i < typeParameters.length; ++i) {
|
|
|
|
var argument = typeArguments[i];
|
|
|
|
var bound = substitution.substituteType(typeParameters[i].bound);
|
|
|
|
checkAssignable(where, argument, bound);
|
|
|
|
}
|
|
|
|
return substitution;
|
|
|
|
}
|
|
|
|
|
2016-11-01 14:43:31 +00:00
|
|
|
@override
|
|
|
|
DartType visitAsExpression(AsExpression node) {
|
|
|
|
visitExpression(node.operand);
|
|
|
|
return node.type;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitAwaitExpression(AwaitExpression node) {
|
|
|
|
return environment.unfutureType(visitExpression(node.operand));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitBoolLiteral(BoolLiteral node) {
|
|
|
|
return environment.boolType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitConditionalExpression(ConditionalExpression node) {
|
2017-01-11 12:47:40 +00:00
|
|
|
node.condition =
|
|
|
|
checkAndDowncastExpression(node.condition, environment.boolType);
|
|
|
|
node.then = checkAndDowncastExpression(node.then, node.staticType);
|
|
|
|
node.otherwise =
|
|
|
|
checkAndDowncastExpression(node.otherwise, node.staticType);
|
2016-11-01 15:04:10 +00:00
|
|
|
return node.staticType;
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitConstructorInvocation(ConstructorInvocation node) {
|
|
|
|
Constructor target = node.target;
|
|
|
|
Arguments arguments = node.arguments;
|
|
|
|
Class class_ = target.enclosingClass;
|
2017-06-07 16:20:50 +00:00
|
|
|
handleCall(arguments, target.function.functionType,
|
2016-11-01 14:43:31 +00:00
|
|
|
typeParameters: class_.typeParameters);
|
|
|
|
return new InterfaceType(target.enclosingClass, arguments.types);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitDirectMethodInvocation(DirectMethodInvocation node) {
|
2017-06-07 16:20:50 +00:00
|
|
|
return handleCall(node.arguments, node.target.getterType,
|
2016-11-01 14:43:31 +00:00
|
|
|
receiver: getReceiverType(node, node.receiver, node.target));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitDirectPropertyGet(DirectPropertyGet node) {
|
|
|
|
var receiver = getReceiverType(node, node.receiver, node.target);
|
|
|
|
return receiver.substituteType(node.target.getterType);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitDirectPropertySet(DirectPropertySet node) {
|
|
|
|
var receiver = getReceiverType(node, node.receiver, node.target);
|
|
|
|
var value = visitExpression(node.value);
|
|
|
|
checkAssignable(node, value,
|
|
|
|
receiver.substituteType(node.target.setterType, contravariant: true));
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitDoubleLiteral(DoubleLiteral node) {
|
|
|
|
return environment.doubleType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitFunctionExpression(FunctionExpression node) {
|
|
|
|
handleNestedFunctionNode(node.function);
|
|
|
|
return node.function.functionType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitIntLiteral(IntLiteral node) {
|
|
|
|
return environment.intType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitInvalidExpression(InvalidExpression node) {
|
2018-01-04 10:17:05 +00:00
|
|
|
return const DynamicType();
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitIsExpression(IsExpression node) {
|
|
|
|
visitExpression(node.operand);
|
|
|
|
return environment.boolType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitLet(Let node) {
|
|
|
|
var value = visitExpression(node.variable.initializer);
|
|
|
|
if (node.variable.type is DynamicType) {
|
|
|
|
node.variable.type = value;
|
|
|
|
}
|
|
|
|
return visitExpression(node.body);
|
|
|
|
}
|
|
|
|
|
2017-12-14 14:51:45 +00:00
|
|
|
@override
|
|
|
|
DartType visitInstantiation(Instantiation node) {
|
|
|
|
DartType type = visitExpression(node.expression);
|
|
|
|
if (type is! FunctionType) {
|
2017-12-15 20:41:43 +00:00
|
|
|
fail(node, 'Not a function type: $type');
|
2017-12-14 14:51:45 +00:00
|
|
|
return const BottomType();
|
|
|
|
}
|
|
|
|
FunctionType functionType = type;
|
|
|
|
if (functionType.typeParameters.length != node.typeArguments.length) {
|
|
|
|
fail(node, 'Wrong number of type arguments');
|
|
|
|
return const BottomType();
|
|
|
|
}
|
2017-12-15 20:41:43 +00:00
|
|
|
return _instantiateFunction(
|
|
|
|
functionType.typeParameters, node.typeArguments, node)
|
|
|
|
.substituteType(functionType.withoutTypeParameters);
|
2017-12-14 14:51:45 +00:00
|
|
|
}
|
|
|
|
|
2016-11-01 14:43:31 +00:00
|
|
|
@override
|
|
|
|
DartType visitListLiteral(ListLiteral node) {
|
2017-01-11 12:47:40 +00:00
|
|
|
for (int i = 0; i < node.expressions.length; ++i) {
|
|
|
|
node.expressions[i] =
|
|
|
|
checkAndDowncastExpression(node.expressions[i], node.typeArgument);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
return environment.literalListType(node.typeArgument);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitLogicalExpression(LogicalExpression node) {
|
2017-01-11 12:47:40 +00:00
|
|
|
node.left = checkAndDowncastExpression(node.left, environment.boolType);
|
|
|
|
node.right = checkAndDowncastExpression(node.right, environment.boolType);
|
2016-11-01 14:43:31 +00:00
|
|
|
return environment.boolType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitMapLiteral(MapLiteral node) {
|
|
|
|
for (var entry in node.entries) {
|
2017-01-11 12:47:40 +00:00
|
|
|
entry.key = checkAndDowncastExpression(entry.key, node.keyType);
|
|
|
|
entry.value = checkAndDowncastExpression(entry.value, node.valueType);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
return environment.literalMapType(node.keyType, node.valueType);
|
|
|
|
}
|
|
|
|
|
|
|
|
DartType handleDynamicCall(DartType receiver, Arguments arguments) {
|
|
|
|
arguments.positional.forEach(visitExpression);
|
|
|
|
arguments.named.forEach((NamedExpression n) => visitExpression(n.value));
|
|
|
|
return const DynamicType();
|
|
|
|
}
|
|
|
|
|
|
|
|
DartType handleFunctionCall(
|
|
|
|
TreeNode access, FunctionType function, Arguments arguments) {
|
|
|
|
if (function.requiredParameterCount > arguments.positional.length) {
|
|
|
|
fail(access, 'Too few positional arguments');
|
|
|
|
return const BottomType();
|
|
|
|
}
|
|
|
|
if (function.positionalParameters.length < arguments.positional.length) {
|
|
|
|
fail(access, 'Too many positional arguments');
|
|
|
|
return const BottomType();
|
|
|
|
}
|
|
|
|
if (function.typeParameters.length != arguments.types.length) {
|
|
|
|
fail(access, 'Wrong number of type arguments');
|
|
|
|
return const BottomType();
|
|
|
|
}
|
|
|
|
var instantiation =
|
|
|
|
Substitution.fromPairs(function.typeParameters, arguments.types);
|
|
|
|
for (int i = 0; i < arguments.positional.length; ++i) {
|
|
|
|
var expectedType = instantiation.substituteType(
|
|
|
|
function.positionalParameters[i],
|
|
|
|
contravariant: true);
|
2017-01-11 12:47:40 +00:00
|
|
|
arguments.positional[i] =
|
|
|
|
checkAndDowncastExpression(arguments.positional[i], expectedType);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
for (int i = 0; i < arguments.named.length; ++i) {
|
|
|
|
var argument = arguments.named[i];
|
2017-05-11 13:03:34 +00:00
|
|
|
var parameterType = function.getNamedParameter(argument.name);
|
|
|
|
if (parameterType != null) {
|
|
|
|
var expectedType =
|
|
|
|
instantiation.substituteType(parameterType, contravariant: true);
|
|
|
|
argument.value =
|
|
|
|
checkAndDowncastExpression(argument.value, expectedType);
|
|
|
|
} else {
|
2016-11-01 14:43:31 +00:00
|
|
|
fail(argument.value, 'Unexpected named parameter: ${argument.name}');
|
2016-11-16 16:09:48 +00:00
|
|
|
return const BottomType();
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return instantiation.substituteType(function.returnType);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitMethodInvocation(MethodInvocation node) {
|
|
|
|
var target = node.interfaceTarget;
|
|
|
|
if (target == null) {
|
|
|
|
var receiver = visitExpression(node.receiver);
|
2016-11-02 08:59:24 +00:00
|
|
|
if (node.name.name == '==') {
|
|
|
|
visitExpression(node.arguments.positional.single);
|
|
|
|
return environment.boolType;
|
|
|
|
}
|
|
|
|
if (node.name.name == 'call' && receiver is FunctionType) {
|
|
|
|
return handleFunctionCall(node, receiver, node.arguments);
|
|
|
|
}
|
2017-10-11 06:35:35 +00:00
|
|
|
checkUnresolvedInvocation(receiver, node);
|
2016-11-02 08:59:24 +00:00
|
|
|
return handleDynamicCall(receiver, node.arguments);
|
2017-06-07 16:20:50 +00:00
|
|
|
} else if (target is Procedure &&
|
|
|
|
environment.isOverloadedArithmeticOperator(target)) {
|
2016-11-01 14:43:31 +00:00
|
|
|
assert(node.arguments.positional.length == 1);
|
|
|
|
var receiver = visitExpression(node.receiver);
|
|
|
|
var argument = visitExpression(node.arguments.positional[0]);
|
|
|
|
return environment.getTypeOfOverloadedArithmetic(receiver, argument);
|
|
|
|
} else {
|
2017-06-07 16:20:50 +00:00
|
|
|
return handleCall(node.arguments, target.getterType,
|
2016-11-01 14:43:31 +00:00
|
|
|
receiver: getReceiverType(node, node.receiver, node.interfaceTarget));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitPropertyGet(PropertyGet node) {
|
|
|
|
if (node.interfaceTarget == null) {
|
2017-10-11 06:35:35 +00:00
|
|
|
final receiver = visitExpression(node.receiver);
|
|
|
|
checkUnresolvedInvocation(receiver, node);
|
2016-11-01 14:43:31 +00:00
|
|
|
return const DynamicType();
|
|
|
|
} else {
|
|
|
|
var receiver = getReceiverType(node, node.receiver, node.interfaceTarget);
|
|
|
|
return receiver.substituteType(node.interfaceTarget.getterType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitPropertySet(PropertySet node) {
|
|
|
|
var value = visitExpression(node.value);
|
|
|
|
if (node.interfaceTarget != null) {
|
|
|
|
var receiver = getReceiverType(node, node.receiver, node.interfaceTarget);
|
|
|
|
checkAssignable(
|
|
|
|
node.value,
|
|
|
|
value,
|
|
|
|
receiver.substituteType(node.interfaceTarget.setterType,
|
|
|
|
contravariant: true));
|
|
|
|
} else {
|
2017-10-11 06:35:35 +00:00
|
|
|
final receiver = visitExpression(node.receiver);
|
|
|
|
checkUnresolvedInvocation(receiver, node);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitNot(Not node) {
|
|
|
|
visitExpression(node.operand);
|
|
|
|
return environment.boolType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitNullLiteral(NullLiteral node) {
|
|
|
|
return const BottomType();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitRethrow(Rethrow node) {
|
|
|
|
return const BottomType();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitStaticGet(StaticGet node) {
|
|
|
|
return node.target.getterType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitStaticInvocation(StaticInvocation node) {
|
2017-06-07 16:20:50 +00:00
|
|
|
return handleCall(node.arguments, node.target.getterType);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitStaticSet(StaticSet node) {
|
|
|
|
var value = visitExpression(node.value);
|
|
|
|
checkAssignable(node.value, value, node.target.setterType);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitStringConcatenation(StringConcatenation node) {
|
|
|
|
node.expressions.forEach(visitExpression);
|
|
|
|
return environment.stringType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitStringLiteral(StringLiteral node) {
|
|
|
|
return environment.stringType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitSuperMethodInvocation(SuperMethodInvocation node) {
|
|
|
|
if (node.interfaceTarget == null) {
|
2017-10-11 06:35:35 +00:00
|
|
|
checkUnresolvedInvocation(environment.thisType, node);
|
2016-11-01 14:43:31 +00:00
|
|
|
return handleDynamicCall(environment.thisType, node.arguments);
|
|
|
|
} else {
|
2017-06-07 16:20:50 +00:00
|
|
|
return handleCall(node.arguments, node.interfaceTarget.getterType,
|
2016-11-01 14:43:31 +00:00
|
|
|
receiver: getSuperReceiverType(node.interfaceTarget));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitSuperPropertyGet(SuperPropertyGet node) {
|
|
|
|
if (node.interfaceTarget == null) {
|
2017-10-11 06:35:35 +00:00
|
|
|
checkUnresolvedInvocation(environment.thisType, node);
|
2016-11-01 14:43:31 +00:00
|
|
|
return const DynamicType();
|
|
|
|
} else {
|
|
|
|
var receiver = getSuperReceiverType(node.interfaceTarget);
|
|
|
|
return receiver.substituteType(node.interfaceTarget.getterType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitSuperPropertySet(SuperPropertySet node) {
|
|
|
|
var value = visitExpression(node.value);
|
|
|
|
if (node.interfaceTarget != null) {
|
|
|
|
var receiver = getSuperReceiverType(node.interfaceTarget);
|
|
|
|
checkAssignable(
|
|
|
|
node.value,
|
|
|
|
value,
|
|
|
|
receiver.substituteType(node.interfaceTarget.setterType,
|
|
|
|
contravariant: true));
|
2017-10-11 06:35:35 +00:00
|
|
|
} else {
|
|
|
|
checkUnresolvedInvocation(environment.thisType, node);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitSymbolLiteral(SymbolLiteral node) {
|
|
|
|
return environment.symbolType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitThisExpression(ThisExpression node) {
|
|
|
|
return environment.thisType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitThrow(Throw node) {
|
|
|
|
visitExpression(node.expression);
|
|
|
|
return const BottomType();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitTypeLiteral(TypeLiteral node) {
|
|
|
|
return environment.typeType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitVariableGet(VariableGet node) {
|
|
|
|
return node.promotedType ?? node.variable.type;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitVariableSet(VariableSet node) {
|
|
|
|
var value = visitExpression(node.value);
|
|
|
|
checkAssignable(node.value, value, node.variable.type);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2017-02-13 13:31:24 +00:00
|
|
|
@override
|
|
|
|
DartType visitLoadLibrary(LoadLibrary node) {
|
|
|
|
return environment.futureType(const DynamicType());
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
DartType visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
|
|
|
|
return environment.objectType;
|
|
|
|
}
|
|
|
|
|
2016-11-01 14:43:31 +00:00
|
|
|
@override
|
|
|
|
visitAssertStatement(AssertStatement node) {
|
|
|
|
visitExpression(node.condition);
|
|
|
|
if (node.message != null) {
|
|
|
|
visitExpression(node.message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitBlock(Block node) {
|
|
|
|
node.statements.forEach(visitStatement);
|
|
|
|
}
|
|
|
|
|
2018-03-14 19:41:09 +00:00
|
|
|
@override
|
|
|
|
visitAssertBlock(AssertBlock node) {
|
|
|
|
node.statements.forEach(visitStatement);
|
|
|
|
}
|
|
|
|
|
2016-11-01 14:43:31 +00:00
|
|
|
@override
|
|
|
|
visitBreakStatement(BreakStatement node) {}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitContinueSwitchStatement(ContinueSwitchStatement node) {}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitDoStatement(DoStatement node) {
|
|
|
|
visitStatement(node.body);
|
2017-01-11 12:47:40 +00:00
|
|
|
node.condition =
|
|
|
|
checkAndDowncastExpression(node.condition, environment.boolType);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitEmptyStatement(EmptyStatement node) {}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitExpressionStatement(ExpressionStatement node) {
|
|
|
|
visitExpression(node.expression);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitForInStatement(ForInStatement node) {
|
|
|
|
var iterable = visitExpression(node.iterable);
|
|
|
|
// TODO(asgerf): Store interface targets on for-in loops or desugar them,
|
|
|
|
// instead of doing the ad-hoc resolution here.
|
|
|
|
if (node.isAsync) {
|
|
|
|
checkAssignable(node, getStreamElementType(iterable), node.variable.type);
|
|
|
|
} else {
|
|
|
|
checkAssignable(
|
|
|
|
node, getIterableElementType(iterable), node.variable.type);
|
|
|
|
}
|
|
|
|
visitStatement(node.body);
|
|
|
|
}
|
|
|
|
|
|
|
|
static final Name iteratorName = new Name('iterator');
|
2017-01-11 12:47:40 +00:00
|
|
|
static final Name currentName = new Name('current');
|
2016-11-01 14:43:31 +00:00
|
|
|
|
|
|
|
DartType getIterableElementType(DartType iterable) {
|
|
|
|
if (iterable is InterfaceType) {
|
|
|
|
var iteratorGetter =
|
|
|
|
hierarchy.getInterfaceMember(iterable.classNode, iteratorName);
|
|
|
|
if (iteratorGetter == null) return const DynamicType();
|
2017-01-11 12:47:40 +00:00
|
|
|
var castedIterable = hierarchy.getTypeAsInstanceOf(
|
|
|
|
iterable, iteratorGetter.enclosingClass);
|
2016-11-01 14:43:31 +00:00
|
|
|
var iteratorType = Substitution
|
2017-01-11 12:47:40 +00:00
|
|
|
.fromInterfaceType(castedIterable)
|
2016-11-01 14:43:31 +00:00
|
|
|
.substituteType(iteratorGetter.getterType);
|
|
|
|
if (iteratorType is InterfaceType) {
|
2017-01-11 12:47:40 +00:00
|
|
|
var currentGetter =
|
|
|
|
hierarchy.getInterfaceMember(iteratorType.classNode, currentName);
|
|
|
|
if (currentGetter == null) return const DynamicType();
|
|
|
|
var castedIteratorType = hierarchy.getTypeAsInstanceOf(
|
|
|
|
iteratorType, currentGetter.enclosingClass);
|
2016-11-01 14:43:31 +00:00
|
|
|
return Substitution
|
2017-01-11 12:47:40 +00:00
|
|
|
.fromInterfaceType(castedIteratorType)
|
|
|
|
.substituteType(currentGetter.getterType);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return const DynamicType();
|
|
|
|
}
|
|
|
|
|
|
|
|
DartType getStreamElementType(DartType stream) {
|
|
|
|
if (stream is InterfaceType) {
|
|
|
|
var asStream =
|
|
|
|
hierarchy.getTypeAsInstanceOf(stream, coreTypes.streamClass);
|
|
|
|
if (asStream == null) return const DynamicType();
|
|
|
|
return asStream.typeArguments.single;
|
|
|
|
}
|
|
|
|
return const DynamicType();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitForStatement(ForStatement node) {
|
|
|
|
node.variables.forEach(visitVariableDeclaration);
|
|
|
|
if (node.condition != null) {
|
2017-01-11 12:47:40 +00:00
|
|
|
node.condition =
|
|
|
|
checkAndDowncastExpression(node.condition, environment.boolType);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
node.updates.forEach(visitExpression);
|
|
|
|
visitStatement(node.body);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitFunctionDeclaration(FunctionDeclaration node) {
|
|
|
|
handleNestedFunctionNode(node.function);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitIfStatement(IfStatement node) {
|
2017-01-11 12:47:40 +00:00
|
|
|
node.condition =
|
|
|
|
checkAndDowncastExpression(node.condition, environment.boolType);
|
2016-11-01 14:43:31 +00:00
|
|
|
visitStatement(node.then);
|
|
|
|
if (node.otherwise != null) {
|
|
|
|
visitStatement(node.otherwise);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitLabeledStatement(LabeledStatement node) {
|
|
|
|
visitStatement(node.body);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitReturnStatement(ReturnStatement node) {
|
|
|
|
if (node.expression != null) {
|
|
|
|
if (environment.returnType == null) {
|
|
|
|
fail(node, 'Return of a value from void method');
|
|
|
|
} else {
|
2016-11-03 14:08:57 +00:00
|
|
|
var type = visitExpression(node.expression);
|
|
|
|
if (environment.currentAsyncMarker == AsyncMarker.Async) {
|
|
|
|
type = environment.unfutureType(type);
|
|
|
|
}
|
|
|
|
checkAssignable(node.expression, type, environment.returnType);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitSwitchStatement(SwitchStatement node) {
|
|
|
|
visitExpression(node.expression);
|
|
|
|
for (var switchCase in node.cases) {
|
|
|
|
switchCase.expressions.forEach(visitExpression);
|
|
|
|
visitStatement(switchCase.body);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitTryCatch(TryCatch node) {
|
|
|
|
visitStatement(node.body);
|
|
|
|
for (var catchClause in node.catches) {
|
|
|
|
visitStatement(catchClause.body);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitTryFinally(TryFinally node) {
|
|
|
|
visitStatement(node.body);
|
|
|
|
visitStatement(node.finalizer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitVariableDeclaration(VariableDeclaration node) {
|
|
|
|
if (node.initializer != null) {
|
2017-01-11 12:47:40 +00:00
|
|
|
node.initializer =
|
|
|
|
checkAndDowncastExpression(node.initializer, node.type);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitWhileStatement(WhileStatement node) {
|
2017-01-11 12:47:40 +00:00
|
|
|
node.condition =
|
|
|
|
checkAndDowncastExpression(node.condition, environment.boolType);
|
2016-11-01 14:43:31 +00:00
|
|
|
visitStatement(node.body);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitYieldStatement(YieldStatement node) {
|
2016-11-03 14:08:57 +00:00
|
|
|
if (node.isYieldStar) {
|
|
|
|
Class container = environment.currentAsyncMarker == AsyncMarker.AsyncStar
|
|
|
|
? coreTypes.streamClass
|
|
|
|
: coreTypes.iterableClass;
|
|
|
|
var type = visitExpression(node.expression);
|
|
|
|
var asContainer = type is InterfaceType
|
|
|
|
? hierarchy.getTypeAsInstanceOf(type, container)
|
|
|
|
: null;
|
|
|
|
if (asContainer != null) {
|
|
|
|
checkAssignable(node.expression, asContainer.typeArguments[0],
|
|
|
|
environment.yieldType);
|
|
|
|
} else {
|
|
|
|
fail(node.expression, '$type is not an instance of $container');
|
|
|
|
}
|
|
|
|
} else {
|
2017-01-11 12:47:40 +00:00
|
|
|
node.expression =
|
|
|
|
checkAndDowncastExpression(node.expression, environment.yieldType);
|
2016-11-03 14:08:57 +00:00
|
|
|
}
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitFieldInitializer(FieldInitializer node) {
|
2017-01-11 12:47:40 +00:00
|
|
|
node.value = checkAndDowncastExpression(node.value, node.field.type);
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitRedirectingInitializer(RedirectingInitializer node) {
|
2017-06-07 16:20:50 +00:00
|
|
|
handleCall(node.arguments, node.target.getterType,
|
2016-11-01 14:43:31 +00:00
|
|
|
typeParameters: const <TypeParameter>[]);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitSuperInitializer(SuperInitializer node) {
|
2017-06-07 16:20:50 +00:00
|
|
|
handleCall(node.arguments, node.target.getterType,
|
2017-01-11 12:47:40 +00:00
|
|
|
typeParameters: const <TypeParameter>[],
|
|
|
|
receiver: getSuperReceiverType(node.target));
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
visitLocalInitializer(LocalInitializer node) {
|
|
|
|
visitVariableDeclaration(node.variable);
|
|
|
|
}
|
|
|
|
|
2017-12-04 11:22:34 +00:00
|
|
|
@override
|
|
|
|
visitAssertInitializer(AssertInitializer node) {
|
|
|
|
visitAssertStatement(node.statement);
|
|
|
|
}
|
|
|
|
|
2016-11-01 14:43:31 +00:00
|
|
|
@override
|
|
|
|
visitInvalidInitializer(InvalidInitializer node) {}
|
[kernel] Add kernel2kernel constant evaluation, binary format as well as vm support
The introduced "constants" transformation can evaluate constant expressions. The
original use-sites of constant expressions are replaced by a new [ConstantExpression]
node, which points to a subclass of a new [Constant] class hierarchy. Constant
[Field]s and [VariableDeclarations]s will be removed, since all use-sites are
re-written.
The [Constant] class hierarchy is, similarly to the [DartType] class hierarchy, not
part of the AST tree (also has no parent pointer). The constants form a
DAG (directed acyclic graph).
There is no canonicalization requirement of the [Constant] objects referenced by the
AST (via [ConstantExpression]). Although it is beneficial to canonicalize them during
construction, since it reduces time spent in operator==/hashCode.
This CL furthermore adds support for a constant table in the binary format. Similarly
to [String]s, we canonicalize the constants before writing the table to the binary.
The constant table entries in the binary are written in a post-order way, to ensure
easy construction on the backend side.
The text format will be augmented with a "constants { ... }" section at the end,
which lists the constants in the same order as in the binary format.
The transformation can be used by those backends who choose to do so. It is not
enabled by default atm. It should therefore not affect analyzer, fasta or other
components.
Change-Id: I57cd9624fedcf537ab6870db76246149647bed21
Reviewed-on: https://dart-review.googlesource.com/14382
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Kevin Millikin <kmillikin@google.com>
2017-11-16 11:08:02 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
visitConstantExpression(ConstantExpression node) {
|
|
|
|
// Without explicitly running the "constants" transformation, we should
|
|
|
|
// never get here!
|
|
|
|
throw 'unreachable';
|
|
|
|
}
|
2016-11-01 14:43:31 +00:00
|
|
|
}
|