blob: a62cd1cfe54900e5d05fb3d5d9c4d186f9cc96e4 [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.
import 'package:kernel/ast.dart' as ir;
import '../closure.dart';
import '../common.dart';
import '../constants/constant_system.dart';
import '../elements/entities.dart';
import '../options.dart';
import '../types/constants.dart';
import '../world.dart';
import 'inferrer_engine.dart';
import 'locals_handler.dart';
import 'type_graph_nodes.dart';
import 'type_system.dart';
/// [KernelTypeGraphBuilder] constructs a type-inference graph for a particular
/// element.
///
/// Calling [run] will start the work of visiting the body of the code to
/// construct a set of inference-nodes that abstractly represent what the code
/// is doing.
class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
final CompilerOptions _options;
final ClosedWorld _closedWorld;
final ClosureDataLookup<ir.Node> _closureDataLookup;
final InferrerEngine<ir.Node> _inferrer;
final TypeSystem<ir.Node> _types;
final MemberEntity _analyzedMember;
final ir.Node _analyzedNode;
LocalsHandler _locals;
TypeInformation _returnType;
KernelTypeGraphBuilder(
this._options,
this._closedWorld,
this._closureDataLookup,
this._inferrer,
this._analyzedMember,
this._analyzedNode,
[this._locals])
: this._types = _inferrer.types {
if (_locals != null) return;
FieldInitializationScope<ir.Node> fieldScope =
_analyzedNode is ir.Constructor
? new FieldInitializationScope(_types)
: null;
_locals = new LocalsHandler(
_inferrer, _types, _options, _analyzedNode, fieldScope);
}
TypeInformation run() {
if (_analyzedMember.isField) {
if (_analyzedNode == null || _analyzedNode is ir.NullLiteral) {
// Eagerly bailout, because computing the closure data only
// works for functions and field assignments.
return _types.nullType;
}
}
// Update the locals that are boxed in [locals]. These locals will
// be handled specially, in that we are computing their LUB at
// each update, and reading them yields the type that was found in a
// previous analysis of [outermostElement].
ClosureRepresentationInfo closureData =
_closureDataLookup.getClosureInfoForMember(_analyzedMember);
closureData.forEachCapturedVariable((variable, field) {
_locals.setCaptured(variable, field);
});
closureData.forEachBoxedVariable((variable, field) {
_locals.setCapturedAndBoxed(variable, field);
});
return _analyzedNode.accept(this);
}
void recordReturnType(TypeInformation type) {
FunctionEntity analyzedMethod = _analyzedMember;
_returnType =
_inferrer.addReturnTypeForMethod(analyzedMethod, _returnType, type);
}
void initializationIsIndefinite() {
MemberEntity member = _analyzedMember;
if (member is ConstructorEntity && member.isGenerativeConstructor) {
_locals.fieldScope.isIndefinite = true;
}
}
TypeInformation visit(ir.Node node) {
return node == null ? null : node.accept(this);
}
@override
TypeInformation visitFunctionNode(ir.FunctionNode node) {
// TODO(redemption): Handle constructors.
// TODO(redemption): Handle native methods.
// TODO(redemption): Set up parameters.
visit(node.body);
switch (node.asyncMarker) {
case ir.AsyncMarker.Sync:
if (_returnType == null) {
// No return in the body.
_returnType = _locals.seenReturnOrThrow
? _types.nonNullEmpty() // Body always throws.
: _types.nullType;
} else if (!_locals.seenReturnOrThrow) {
// We haven'TypeInformation seen returns on all branches. So the method may
// also return null.
recordReturnType(_types.nullType);
}
break;
case ir.AsyncMarker.SyncStar:
// TODO(asgerf): Maybe make a ContainerTypeMask for these? The type
// contained is the method body's return type.
recordReturnType(_types.syncStarIterableType);
break;
case ir.AsyncMarker.Async:
recordReturnType(_types.asyncFutureType);
break;
case ir.AsyncMarker.AsyncStar:
recordReturnType(_types.asyncStarStreamType);
break;
case ir.AsyncMarker.SyncYielding:
failedAt(
_analyzedMember, "Unexpected async marker: ${node.asyncMarker}");
break;
}
return _returnType;
}
@override
TypeInformation defaultExpression(ir.Expression expression) {
// TODO(efortuna): Remove when more is implemented.
return _types.dynamicType;
}
@override
TypeInformation visitNullLiteral(ir.NullLiteral literal) {
return _types.nullType;
}
@override
TypeInformation visitBlock(ir.Block block) {
for (ir.Statement statement in block.statements) {
statement.accept(this);
if (_locals.aborts) break;
}
return null;
}
@override
TypeInformation visitListLiteral(ir.ListLiteral listLiteral) {
// We only set the type once. We don't need to re-visit the children
// when re-analyzing the node.
return _inferrer.concreteTypes.putIfAbsent(listLiteral, () {
TypeInformation elementType;
int length = 0;
for (ir.Expression element in listLiteral.expressions) {
TypeInformation type = element.accept(this);
elementType = elementType == null
? _types.allocatePhi(null, null, type, isTry: false)
: _types.addPhiInput(null, elementType, type);
length++;
}
elementType = elementType == null
? _types.nonNullEmpty()
: _types.simplifyPhi(null, null, elementType);
TypeInformation containerType =
listLiteral.isConst ? _types.constListType : _types.growableListType;
return _types.allocateList(
containerType, listLiteral, _analyzedMember, elementType, length);
});
}
@override
TypeInformation visitReturnStatement(ir.ReturnStatement node) {
ir.Node expression = node.expression;
recordReturnType(
expression == null ? _types.nullType : expression.accept(this));
_locals.seenReturnOrThrow = true;
initializationIsIndefinite();
return null;
}
@override
TypeInformation visitIntLiteral(ir.IntLiteral node) {
ConstantSystem constantSystem = _closedWorld.constantSystem;
// The JavaScript backend may turn this literal into a double at
// runtime.
return _types.getConcreteTypeFor(
computeTypeMask(_closedWorld, constantSystem.createInt(node.value)));
}
@override
TypeInformation visitDoubleLiteral(ir.DoubleLiteral node) {
ConstantSystem constantSystem = _closedWorld.constantSystem;
// The JavaScript backend may turn this literal into an integer at
// runtime.
return _types.getConcreteTypeFor(
computeTypeMask(_closedWorld, constantSystem.createDouble(node.value)));
}
}