dart-sdk/pkg/kernel/lib/type_checker.dart

993 lines
31 KiB
Dart
Raw Normal View History

// 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;
final bool ignoreSdk;
TypeEnvironment environment;
TypeChecker(this.coreTypes, this.hierarchy,
{bool strongMode: false, this.ignoreSdk: true}) {
environment =
new TypeEnvironment(coreTypes, hierarchy, strongMode: strongMode);
}
void checkComponent(Component component) {
for (var library in component.libraries) {
if (ignoreSdk && library.importUri.scheme == 'dart') continue;
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);
for (var library in component.libraries) {
if (ignoreSdk && library.importUri.scheme == 'dart') continue;
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);
}
/// Check that [ownMember] of [host] can override [superMember].
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);
/// 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;
}
/// 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.
}
/// 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);
}
void checkUnresolvedInvocation(DartType receiver, TreeNode where) {
checker.checkUnresolvedInvocation(receiver, where);
}
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);
}
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) {
node.initializer =
checkAndDowncastExpression(node.initializer, node.type);
}
}
visitConstructor(Constructor node) {
environment.returnType = null;
environment.yieldType = null;
node.initializers.forEach(visitInitializer);
handleFunctionNode(node.function);
}
visitProcedure(Procedure node) {
environment.returnType = _getInternalReturnType(node.function);
environment.yieldType = _getYieldType(node.function);
handleFunctionNode(node.function);
}
visitRedirectingFactoryConstructor(RedirectingFactoryConstructor node) {
environment.returnType = null;
environment.yieldType = null;
}
void handleFunctionNode(FunctionNode node) {
var oldAsyncMarker = environment.currentAsyncMarker;
environment.currentAsyncMarker = node.asyncMarker;
node.positionalParameters
.skip(node.requiredParameterCount)
.forEach(handleOptionalParameter);
node.namedParameters.forEach(handleOptionalParameter);
if (node.body != null) {
visitStatement(node.body);
}
environment.currentAsyncMarker = oldAsyncMarker;
}
void handleNestedFunctionNode(FunctionNode node) {
var oldReturn = environment.returnType;
var oldYield = environment.yieldType;
environment.returnType = _getInternalReturnType(node);
environment.yieldType = _getYieldType(node);
handleFunctionNode(node);
environment.returnType = oldReturn;
environment.yieldType = oldYield;
}
void handleOptionalParameter(VariableDeclaration parameter) {
if (parameter.initializer != null) {
// Default parameter values cannot be downcast.
checkExpressionNoDowncast(parameter.initializer, parameter.type);
}
}
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) {
type = (type as TypeParameterType).bound;
}
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));
}
DartType handleCall(Arguments arguments, DartType functionType,
{Substitution receiver: Substitution.empty,
List<TypeParameter> typeParameters}) {
if (functionType is FunctionType) {
typeParameters ??= functionType.typeParameters;
if (arguments.positional.length < functionType.requiredParameterCount) {
fail(arguments, 'Too few positional arguments');
return const BottomType();
}
if (arguments.positional.length >
functionType.positionalParameters.length) {
fail(arguments, 'Too many positional arguments');
return const BottomType();
}
var typeArguments = arguments.types;
if (typeArguments.length != typeParameters.length) {
fail(arguments, 'Wrong number of type arguments');
return const BottomType();
}
Substitution substitution = _instantiateFunction(
typeParameters, typeArguments, arguments,
receiverSubstitution: receiver);
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();
}
}
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:
return null;
case AsyncMarker.SyncYielding:
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;
}
return null;
default:
throw 'Unexpected async marker: ${function.asyncMarker}';
}
}
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}';
}
}
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;
}
@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) {
node.condition =
checkAndDowncastExpression(node.condition, environment.boolType);
node.then = checkAndDowncastExpression(node.then, node.staticType);
node.otherwise =
checkAndDowncastExpression(node.otherwise, node.staticType);
return node.staticType;
}
@override
DartType visitConstructorInvocation(ConstructorInvocation node) {
Constructor target = node.target;
Arguments arguments = node.arguments;
Class class_ = target.enclosingClass;
handleCall(arguments, target.function.functionType,
typeParameters: class_.typeParameters);
return new InterfaceType(target.enclosingClass, arguments.types);
}
@override
DartType visitDirectMethodInvocation(DirectMethodInvocation node) {
return handleCall(node.arguments, node.target.getterType,
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) {
return const DynamicType();
}
@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);
}
@override
DartType visitInstantiation(Instantiation node) {
DartType type = visitExpression(node.expression);
if (type is! FunctionType) {
fail(node, 'Not a function type: $type');
return const BottomType();
}
FunctionType functionType = type;
if (functionType.typeParameters.length != node.typeArguments.length) {
fail(node, 'Wrong number of type arguments');
return const BottomType();
}
return _instantiateFunction(
functionType.typeParameters, node.typeArguments, node)
.substituteType(functionType.withoutTypeParameters);
}
@override
DartType visitListLiteral(ListLiteral node) {
for (int i = 0; i < node.expressions.length; ++i) {
node.expressions[i] =
checkAndDowncastExpression(node.expressions[i], node.typeArgument);
}
return environment.literalListType(node.typeArgument);
}
@override
DartType visitLogicalExpression(LogicalExpression node) {
node.left = checkAndDowncastExpression(node.left, environment.boolType);
node.right = checkAndDowncastExpression(node.right, environment.boolType);
return environment.boolType;
}
@override
DartType visitMapLiteral(MapLiteral node) {
for (var entry in node.entries) {
entry.key = checkAndDowncastExpression(entry.key, node.keyType);
entry.value = checkAndDowncastExpression(entry.value, node.valueType);
}
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);
arguments.positional[i] =
checkAndDowncastExpression(arguments.positional[i], expectedType);
}
for (int i = 0; i < arguments.named.length; ++i) {
var argument = arguments.named[i];
var parameterType = function.getNamedParameter(argument.name);
if (parameterType != null) {
var expectedType =
instantiation.substituteType(parameterType, contravariant: true);
argument.value =
checkAndDowncastExpression(argument.value, expectedType);
} else {
fail(argument.value, 'Unexpected named parameter: ${argument.name}');
return const BottomType();
}
}
return instantiation.substituteType(function.returnType);
}
@override
DartType visitMethodInvocation(MethodInvocation node) {
var target = node.interfaceTarget;
if (target == null) {
var receiver = visitExpression(node.receiver);
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);
}
checkUnresolvedInvocation(receiver, node);
return handleDynamicCall(receiver, node.arguments);
} else if (target is Procedure &&
environment.isOverloadedArithmeticOperator(target)) {
assert(node.arguments.positional.length == 1);
var receiver = visitExpression(node.receiver);
var argument = visitExpression(node.arguments.positional[0]);
return environment.getTypeOfOverloadedArithmetic(receiver, argument);
} else {
return handleCall(node.arguments, target.getterType,
receiver: getReceiverType(node, node.receiver, node.interfaceTarget));
}
}
@override
DartType visitPropertyGet(PropertyGet node) {
if (node.interfaceTarget == null) {
final receiver = visitExpression(node.receiver);
checkUnresolvedInvocation(receiver, node);
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 {
final receiver = visitExpression(node.receiver);
checkUnresolvedInvocation(receiver, node);
}
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) {
return handleCall(node.arguments, node.target.getterType);
}
@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) {
checkUnresolvedInvocation(environment.thisType, node);
return handleDynamicCall(environment.thisType, node.arguments);
} else {
return handleCall(node.arguments, node.interfaceTarget.getterType,
receiver: getSuperReceiverType(node.interfaceTarget));
}
}
@override
DartType visitSuperPropertyGet(SuperPropertyGet node) {
if (node.interfaceTarget == null) {
checkUnresolvedInvocation(environment.thisType, node);
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));
} else {
checkUnresolvedInvocation(environment.thisType, node);
}
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;
}
@override
DartType visitLoadLibrary(LoadLibrary node) {
return environment.futureType(const DynamicType());
}
@override
DartType visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
return environment.objectType;
}
@override
visitAssertStatement(AssertStatement node) {
visitExpression(node.condition);
if (node.message != null) {
visitExpression(node.message);
}
}
@override
visitBlock(Block node) {
node.statements.forEach(visitStatement);
}
@override
visitAssertBlock(AssertBlock node) {
node.statements.forEach(visitStatement);
}
@override
visitBreakStatement(BreakStatement node) {}
@override
visitContinueSwitchStatement(ContinueSwitchStatement node) {}
@override
visitDoStatement(DoStatement node) {
visitStatement(node.body);
node.condition =
checkAndDowncastExpression(node.condition, environment.boolType);
}
@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');
static final Name currentName = new Name('current');
DartType getIterableElementType(DartType iterable) {
if (iterable is InterfaceType) {
var iteratorGetter =
hierarchy.getInterfaceMember(iterable.classNode, iteratorName);
if (iteratorGetter == null) return const DynamicType();
var castedIterable = hierarchy.getTypeAsInstanceOf(
iterable, iteratorGetter.enclosingClass);
var iteratorType = Substitution
.fromInterfaceType(castedIterable)
.substituteType(iteratorGetter.getterType);
if (iteratorType is InterfaceType) {
var currentGetter =
hierarchy.getInterfaceMember(iteratorType.classNode, currentName);
if (currentGetter == null) return const DynamicType();
var castedIteratorType = hierarchy.getTypeAsInstanceOf(
iteratorType, currentGetter.enclosingClass);
return Substitution
.fromInterfaceType(castedIteratorType)
.substituteType(currentGetter.getterType);
}
}
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) {
node.condition =
checkAndDowncastExpression(node.condition, environment.boolType);
}
node.updates.forEach(visitExpression);
visitStatement(node.body);
}
@override
visitFunctionDeclaration(FunctionDeclaration node) {
handleNestedFunctionNode(node.function);
}
@override
visitIfStatement(IfStatement node) {
node.condition =
checkAndDowncastExpression(node.condition, environment.boolType);
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 {
var type = visitExpression(node.expression);
if (environment.currentAsyncMarker == AsyncMarker.Async) {
type = environment.unfutureType(type);
}
checkAssignable(node.expression, type, environment.returnType);
}
}
}
@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) {
node.initializer =
checkAndDowncastExpression(node.initializer, node.type);
}
}
@override
visitWhileStatement(WhileStatement node) {
node.condition =
checkAndDowncastExpression(node.condition, environment.boolType);
visitStatement(node.body);
}
@override
visitYieldStatement(YieldStatement node) {
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 {
node.expression =
checkAndDowncastExpression(node.expression, environment.yieldType);
}
}
@override
visitFieldInitializer(FieldInitializer node) {
node.value = checkAndDowncastExpression(node.value, node.field.type);
}
@override
visitRedirectingInitializer(RedirectingInitializer node) {
handleCall(node.arguments, node.target.getterType,
typeParameters: const <TypeParameter>[]);
}
@override
visitSuperInitializer(SuperInitializer node) {
handleCall(node.arguments, node.target.getterType,
typeParameters: const <TypeParameter>[],
receiver: getSuperReceiverType(node.target));
}
@override
visitLocalInitializer(LocalInitializer node) {
visitVariableDeclaration(node.variable);
}
@override
visitAssertInitializer(AssertInitializer node) {
visitAssertStatement(node.statement);
}
@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';
}
}