Split ast based parts from InferrerEngineImpl

R=sigmund@google.com

Review-Url: https://codereview.chromium.org/2991313002 .
This commit is contained in:
Johnni Winther 2017-08-04 09:57:30 +02:00
parent 1df5b00122
commit 93d0a55534
7 changed files with 641 additions and 558 deletions

View file

@ -0,0 +1,530 @@
// 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 '../common.dart';
import '../common/names.dart';
import '../compiler.dart';
import '../constants/expressions.dart';
import '../constants/values.dart';
import '../elements/elements.dart';
import '../elements/entities.dart';
import '../resolution/tree_elements.dart';
import '../tree/nodes.dart' as ast;
import '../types/constants.dart';
import '../types/types.dart';
import '../universe/selector.dart';
import '../util/util.dart';
import '../world.dart';
import 'closure_tracer.dart';
import 'debug.dart' as debug;
import 'locals_handler.dart';
import 'builder.dart';
import 'builder_kernel.dart';
import 'inferrer_engine.dart';
import 'type_graph_dump.dart';
import 'type_graph_nodes.dart';
import 'type_system.dart';
class AstInferrerEngine extends InferrerEngineImpl<ast.Node> {
AstInferrerEngine(Compiler compiler, ClosedWorld closedWorld,
ClosedWorldRefiner closedWorldRefiner, FunctionEntity mainElement)
: super(compiler, closedWorld, closedWorldRefiner, mainElement,
const TypeSystemStrategyImpl());
GlobalTypeInferenceElementData<ast.Node> createElementData() =>
new AstGlobalTypeInferenceElementData();
void runOverAllElements() {
if (compiler.disableTypeInference) return;
if (compiler.options.verbose) {
compiler.progress.reset();
}
sortResolvedAsts().forEach((ResolvedAst resolvedAst) {
if (compiler.shouldPrintProgress) {
reporter.log('Added $addedInGraph elements in inferencing graph.');
compiler.progress.reset();
}
// This also forces the creation of the [ElementTypeInformation] to ensure
// it is in the graph.
MemberElement member = resolvedAst.element;
ast.Node body;
if (resolvedAst.kind == ResolvedAstKind.PARSED) {
body = resolvedAst.body;
}
types.withMember(member, () => analyze(member, body, null));
});
reporter.log('Added $addedInGraph elements in inferencing graph.');
TypeGraphDump dump = debug.PRINT_GRAPH ? new TypeGraphDump(this) : null;
dump?.beforeAnalysis();
buildWorkQueue();
refine();
// Try to infer element types of lists and compute their escape information.
types.allocatedLists.values.forEach((TypeInformation info) {
analyzeListAndEnqueue(info);
});
// Try to infer the key and value types for maps and compute the values'
// escape information.
types.allocatedMaps.values.forEach((TypeInformation info) {
analyzeMapAndEnqueue(info);
});
Set<FunctionEntity> bailedOutOn = new Set<FunctionEntity>();
// Trace closures to potentially infer argument types.
types.allocatedClosures.forEach((dynamic info) {
void trace(
Iterable<FunctionEntity> elements, ClosureTracerVisitor tracer) {
tracer.run();
if (!tracer.continueAnalyzing) {
elements.forEach((FunctionEntity _element) {
MethodElement element = _element;
MethodElement implementation = element.implementation;
closedWorldRefiner.registerMightBePassedToApply(element);
if (debug.VERBOSE) {
print("traced closure $element as ${true} (bail)");
}
implementation.functionSignature
.forEachParameter((FormalElement _parameter) {
ParameterElement parameter = _parameter;
types
.getInferredTypeOfParameter(parameter)
.giveUp(this, clearAssignments: false);
});
});
bailedOutOn.addAll(elements);
return;
}
elements
.where((e) => !bailedOutOn.contains(e))
.forEach((FunctionEntity _element) {
MethodElement element = _element;
MethodElement implementation = element.implementation;
implementation.functionSignature
.forEachParameter((FormalElement _parameter) {
ParameterElement parameter = _parameter;
ParameterTypeInformation info =
types.getInferredTypeOfParameter(parameter);
info.maybeResume();
workQueue.add(info);
});
if (tracer.tracedType.mightBePassedToFunctionApply) {
closedWorldRefiner.registerMightBePassedToApply(element);
}
if (debug.VERBOSE) {
print("traced closure $element as "
"${closedWorldRefiner
.getCurrentlyKnownMightBePassedToApply(element)}");
}
});
}
if (info is ClosureTypeInformation) {
Iterable<FunctionEntity> elements = [info.closure];
trace(elements, new ClosureTracerVisitor(elements, info, this));
} else if (info is CallSiteTypeInformation) {
if (info is StaticCallSiteTypeInformation &&
info.selector != null &&
info.selector.isCall) {
// This is a constructor call to a class with a call method. So we
// need to trace the call method here.
MethodElement calledElement = info.calledElement;
assert(calledElement.isGenerativeConstructor);
ClassElement cls = calledElement.enclosingClass;
MethodElement callMethod = cls.lookupMember(Identifiers.call);
if (callMethod == null) {
callMethod = cls.lookupMember(Identifiers.noSuchMethod_);
}
assert(callMethod != null, failedAt(cls));
Iterable<FunctionEntity> elements = [callMethod];
trace(elements, new ClosureTracerVisitor(elements, info, this));
} else {
// We only are interested in functions here, as other targets
// of this closure call are not a root to trace but an intermediate
// for some other function.
Iterable<FunctionEntity> elements = new List<FunctionEntity>.from(
info.callees.where((e) => e.isFunction));
trace(elements, new ClosureTracerVisitor(elements, info, this));
}
} else if (info is MemberTypeInformation) {
trace(<FunctionEntity>[info.member],
new StaticTearOffClosureTracerVisitor(info.member, info, this));
} else if (info is ParameterTypeInformation) {
failedAt(
NO_LOCATION_SPANNABLE, 'Unexpected closure allocation info $info');
}
});
dump?.beforeTracing();
// Reset all nodes that use lists/maps that have been inferred, as well
// as nodes that use elements fetched from these lists/maps. The
// workset for a new run of the analysis will be these nodes.
Set<TypeInformation> seenTypes = new Set<TypeInformation>();
while (!workQueue.isEmpty) {
TypeInformation info = workQueue.remove();
if (seenTypes.contains(info)) continue;
// If the node cannot be reset, we do not need to update its users either.
if (!info.reset(this)) continue;
seenTypes.add(info);
workQueue.addAll(info.users);
}
workQueue.addAll(seenTypes);
refine();
if (debug.PRINT_SUMMARY) {
types.allocatedLists.values.forEach((_info) {
ListTypeInformation info = _info;
print('${info.type} '
'for ${info.originalType.allocationNode} '
'at ${info.originalType.allocationElement} '
'after ${info.refineCount}');
});
types.allocatedMaps.values.forEach((_info) {
MapTypeInformation info = _info;
print('${info.type} '
'for ${info.originalType.allocationNode} '
'at ${info.originalType.allocationElement} '
'after ${info.refineCount}');
});
types.allocatedClosures.forEach((TypeInformation info) {
if (info is ElementTypeInformation) {
print('${info.getInferredSignature(types)} for '
'${info.debugName}');
} else if (info is ClosureTypeInformation) {
print('${info.getInferredSignature(types)} for '
'${info.debugName}');
} else if (info is DynamicCallSiteTypeInformation) {
for (MemberEntity target in info.targets) {
if (target is FunctionEntity) {
print(
'${types.getInferredSignatureOfMethod(target)} for ${target}');
} else {
print(
'${types.getInferredTypeOfMember(target).type} for ${target}');
}
}
} else if (info is StaticCallSiteTypeInformation) {
ClassElement cls = info.calledElement.enclosingClass;
MethodElement callMethod = cls.lookupMember(Identifiers.call);
print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}');
} else {
print('${info.type} for some unknown kind of closure');
}
});
analyzedElements.forEach((MemberEntity elem) {
TypeInformation type = types.getInferredTypeOfMember(elem);
print('${elem} :: ${type} from ${type.assignments} ');
});
}
dump?.afterAnalysis();
reporter.log('Inferred $overallRefineCount types.');
processLoopInformation();
}
void analyze(MemberEntity element, ast.Node body, ArgumentsTypes arguments) {
assert(!(element is MemberElement && !element.isDeclaration));
if (analyzedElements.contains(element)) return;
analyzedElements.add(element);
dynamic visitor = compiler.options.kernelGlobalInference
? new KernelTypeGraphBuilder(element, compiler, this)
: new ElementGraphBuilder(element, compiler, this);
TypeInformation type;
reporter.withCurrentElement(element, () {
// ignore: UNDEFINED_METHOD
type = visitor.run();
});
addedInGraph++;
if (element.isField) {
FieldElement field = element;
if (field.isFinal || field.isConst) {
// If [element] is final and has an initializer, we record
// the inferred type.
if (body != null) {
if (type is! ListTypeInformation && type is! MapTypeInformation) {
// For non-container types, the constant handler does
// constant folding that could give more precise results.
ConstantExpression constant = field.constant;
if (constant != null) {
ConstantValue value =
compiler.backend.constants.getConstantValue(constant);
if (value != null) {
if (value.isFunction) {
FunctionConstantValue functionConstant = value;
MethodElement function = functionConstant.element;
type = types.allocateClosure(function);
} else {
// Although we might find a better type, we have to keep
// the old type around to ensure that we get a complete view
// of the type graph and do not drop any flow edges.
TypeMask refinedType = computeTypeMask(closedWorld, value);
assert(TypeMask.assertIsNormalized(refinedType, closedWorld));
type = new NarrowTypeInformation(type, refinedType);
types.allocatedTypes.add(type);
}
} else {
assert(
field.isInstanceMember ||
constant.isImplicit ||
constant.isPotential,
failedAt(
field,
"Constant expression without value: "
"${constant.toStructuredText()}."));
}
}
}
recordTypeOfField(field, type);
} else if (!element.isInstanceMember) {
recordTypeOfField(field, types.nullType);
}
} else if (body == null) {
// Only update types of static fields if there is no
// assignment. Instance fields are dealt with in the constructor.
if (element.isStatic || element.isTopLevel) {
recordTypeOfField(field, type);
}
} else {
recordTypeOfField(field, type);
}
if ((element.isStatic || element.isTopLevel) &&
body != null &&
!element.isConst) {
dynamic argument = body;
// TODO(13429): We could do better here by using the
// constant handler to figure out if it's a lazy field or not.
if (argument.asSend() != null ||
(argument.asNewExpression() != null && !argument.isConst)) {
recordTypeOfField(field, types.nullType);
}
}
} else {
FunctionEntity method = element;
recordReturnType(method, type);
}
}
void updateParameterAssignments(TypeInformation caller, MemberEntity callee,
ArgumentsTypes arguments, Selector selector, TypeMask mask,
{bool remove, bool addToQueue: true}) {
if (callee.name == Identifiers.noSuchMethod_) return;
if (callee.isField) {
if (selector.isSetter) {
ElementTypeInformation info = types.getInferredTypeOfMember(callee);
if (remove) {
info.removeAssignment(arguments.positional[0]);
} else {
info.addAssignment(arguments.positional[0]);
}
if (addToQueue) workQueue.add(info);
}
} else if (callee.isGetter) {
return;
} else if (selector != null && selector.isGetter) {
// We are tearing a function off and thus create a closure.
assert(callee.isFunction);
MethodElement method = callee;
MemberTypeInformation info = types.getInferredTypeOfMember(method);
if (remove) {
info.closurizedCount--;
} else {
info.closurizedCount++;
if (Elements.isStaticOrTopLevel(method)) {
types.allocatedClosures.add(info);
} else {
// We add the call-site type information here so that we
// can benefit from further refinement of the selector.
types.allocatedClosures.add(caller);
}
FunctionElement function = method.implementation;
FunctionSignature signature = function.functionSignature;
signature.forEachParameter((FormalElement _parameter) {
ParameterElement parameter = _parameter;
ParameterTypeInformation info =
types.getInferredTypeOfParameter(parameter);
info.tagAsTearOffClosureParameter(this);
if (addToQueue) workQueue.add(info);
});
}
} else {
MethodElement method = callee;
FunctionElement function = method.implementation;
FunctionSignature signature = function.functionSignature;
int parameterIndex = 0;
bool visitingRequiredParameter = true;
signature.forEachParameter((FormalElement _parameter) {
ParameterElement parameter = _parameter;
if (signature.hasOptionalParameters &&
parameter == signature.optionalParameters.first) {
visitingRequiredParameter = false;
}
TypeInformation type = visitingRequiredParameter
? arguments.positional[parameterIndex]
: signature.optionalParametersAreNamed
? arguments.named[parameter.name]
: parameterIndex < arguments.positional.length
? arguments.positional[parameterIndex]
: null;
if (type == null) type = getDefaultTypeOfParameter(parameter);
TypeInformation info = types.getInferredTypeOfParameter(parameter);
if (remove) {
info.removeAssignment(type);
} else {
info.addAssignment(type);
}
parameterIndex++;
if (addToQueue) workQueue.add(info);
});
}
}
// Sorts the resolved elements by size. We do this for this inferrer
// to get the same results for [ListTracer] compared to the
// [SimpleTypesInferrer].
Iterable<ResolvedAst> sortResolvedAsts() {
int max = 0;
Map<int, Setlet<ResolvedAst>> methodSizes = <int, Setlet<ResolvedAst>>{};
compiler.enqueuer.resolution.processedEntities.forEach((_element) {
MemberElement element = _element;
ResolvedAst resolvedAst = element.resolvedAst;
element = element.implementation;
if (element.impliesType) return;
assert(
element.isField ||
element.isFunction ||
element.isConstructor ||
element.isGetter ||
element.isSetter,
failedAt(element, 'Unexpected element kind: ${element.kind}'));
if (element.isAbstract) return;
// Put the other operators in buckets by length, later to be added in
// length order.
int length = 0;
if (resolvedAst.kind == ResolvedAstKind.PARSED) {
TreeElementMapping mapping = resolvedAst.elements;
length = mapping.getSelectorCount();
}
max = length > max ? length : max;
Setlet<ResolvedAst> set =
methodSizes.putIfAbsent(length, () => new Setlet<ResolvedAst>());
set.add(resolvedAst);
});
List<ResolvedAst> result = <ResolvedAst>[];
for (int i = 0; i <= max; i++) {
Setlet<ResolvedAst> set = methodSizes[i];
if (set != null) result.addAll(set);
}
return result;
}
}
class TypeSystemStrategyImpl implements TypeSystemStrategy<ast.Node> {
const TypeSystemStrategyImpl();
@override
MemberTypeInformation createMemberTypeInformation(
covariant MemberElement member) {
assert(member.isDeclaration, failedAt(member));
if (member.isField) {
FieldElement field = member;
return new FieldTypeInformation(field, field.type);
} else if (member.isGetter) {
GetterElement getter = member;
return new GetterTypeInformation(getter, getter.type);
} else if (member.isSetter) {
SetterElement setter = member;
return new SetterTypeInformation(setter);
} else if (member.isFunction) {
MethodElement method = member;
return new MethodTypeInformation(method, method.type);
} else {
ConstructorElement constructor = member;
if (constructor.isFactoryConstructor) {
return new FactoryConstructorTypeInformation(
constructor, constructor.type);
} else {
return new GenerativeConstructorTypeInformation(constructor);
}
}
}
@override
ParameterTypeInformation createParameterTypeInformation(
covariant ParameterElement parameter, TypeSystem<ast.Node> types) {
assert(parameter.isImplementation, failedAt(parameter));
FunctionTypedElement function = parameter.functionDeclaration.declaration;
if (function.isLocal) {
LocalFunctionElement localFunction = function;
MethodElement callMethod = localFunction.callMethod;
return new ParameterTypeInformation.localFunction(
types.getInferredTypeOfMember(callMethod),
parameter,
parameter.type,
callMethod);
} else if (function.isInstanceMember) {
MethodElement method = function;
return new ParameterTypeInformation.instanceMember(
types.getInferredTypeOfMember(method),
parameter,
parameter.type,
method,
new ParameterAssignments());
} else {
MethodElement method = function;
return new ParameterTypeInformation.static(
types.getInferredTypeOfMember(method),
parameter,
parameter.type,
method,
// TODO(johnniwinther): Is this still valid now that initializing
// formals also introduce locals?
isInitializingFormal: parameter.isInitializingFormal);
}
}
@override
void forEachParameter(
covariant MethodElement function, void f(Local parameter)) {
MethodElement impl = function.implementation;
FunctionSignature signature = impl.functionSignature;
signature.forEachParameter((FormalElement _parameter) {
ParameterElement parameter = _parameter;
f(parameter);
});
}
@override
bool checkMapNode(ast.Node node) {
return node is ast.LiteralMap;
}
@override
bool checkListNode(ast.Node node) {
return node is ast.LiteralList || node is ast.Send;
}
@override
bool checkLoopPhiNode(ast.Node node) {
return node is ast.Loop || node is ast.SwitchStatement;
}
@override
bool checkPhiNode(ast.Node node) {
return true;
}
@override
bool checkClassEntity(covariant ClassElement cls) {
return cls.isDeclaration;
}
}

View file

@ -104,10 +104,11 @@ class ElementGraphBuilder extends ast.Visitor<TypeInformation>
new LocalsHandler(inferrer, types, compiler.options, node, fieldScope);
}
ElementGraphBuilder(MemberElement element, ResolvedAst resolvedAst,
Compiler compiler, InferrerEngine inferrer, [LocalsHandler handler])
: this.internal(element, resolvedAst, element.memberContext.declaration,
inferrer, compiler, handler);
ElementGraphBuilder(
MemberElement element, Compiler compiler, InferrerEngine inferrer,
[LocalsHandler handler])
: this.internal(element, element.resolvedAst,
element.memberContext.declaration, inferrer, compiler, handler);
TreeElements get elements => resolvedAst.elements;
@ -892,8 +893,13 @@ class ElementGraphBuilder extends ast.Visitor<TypeInformation>
void analyzeSuperConstructorCall(
ConstructorElement target, ArgumentsTypes arguments) {
assert(target.isDeclaration);
ResolvedAst resolvedAst = target.resolvedAst;
inferrer.analyze(resolvedAst, arguments);
ast.Node body;
if (resolvedAst.kind == ResolvedAstKind.PARSED) {
body = resolvedAst.node;
}
inferrer.analyze(target, body, arguments);
isThisExposed = isThisExposed || inferrer.checkIfExposesThis(target);
}
@ -956,8 +962,8 @@ class ElementGraphBuilder extends ast.Visitor<TypeInformation>
parameter,
"Unexpected function declaration "
"${declarationMethod}, expected ${analyzedElement}."));
visitor = new ElementGraphBuilder(declarationMethod,
parameter.functionDeclaration.resolvedAst, compiler, inferrer);
visitor =
new ElementGraphBuilder(declarationMethod, compiler, inferrer);
}
TypeInformation type =
(defaultValue == null) ? types.nullType : visitor.visit(defaultValue);
@ -1104,8 +1110,8 @@ class ElementGraphBuilder extends ast.Visitor<TypeInformation>
// method, like for example the types of local variables.
LocalsHandler closureLocals =
new LocalsHandler.from(locals, node, useOtherTryBlock: false);
ElementGraphBuilder visitor = new ElementGraphBuilder(element.callMethod,
element.resolvedAst, compiler, inferrer, closureLocals);
ElementGraphBuilder visitor = new ElementGraphBuilder(
element.callMethod, compiler, inferrer, closureLocals);
visitor.run();
inferrer.recordReturnType(element.callMethod, visitor.returnType);

View file

@ -65,14 +65,14 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
new LocalsHandler(inferrer, types, compiler.options, node, fieldScope);
}
factory KernelTypeGraphBuilder(MemberElement element, ResolvedAst resolvedAst,
Compiler compiler, InferrerEngine inferrer,
factory KernelTypeGraphBuilder(
MemberElement element, Compiler compiler, InferrerEngine inferrer,
[LocalsHandler handler]) {
var adapter = _createKernelAdapter(compiler, resolvedAst);
var adapter = _createKernelAdapter(compiler, element.resolvedAst);
var node = adapter.getMemberNode(element);
return new KernelTypeGraphBuilder.internal(
element,
resolvedAst,
element.resolvedAst,
element.outermostEnclosingMemberOrTopLevel.implementation,
inferrer,
compiler,

View file

@ -7,32 +7,29 @@ import 'package:kernel/ast.dart' as ir;
import '../common.dart';
import '../common/names.dart';
import '../compiler.dart';
import '../constants/expressions.dart';
import '../constants/values.dart';
import '../common_elements.dart';
import '../elements/elements.dart';
import '../elements/elements.dart'
show
ClassElement,
ConstructorElement,
Elements,
MemberElement,
ParameterElement;
import '../elements/entities.dart';
import '../elements/names.dart';
import '../js_backend/annotations.dart';
import '../js_backend/js_backend.dart';
import '../native/behavior.dart' as native;
import '../resolution/tree_elements.dart';
import '../tree/nodes.dart' as ast;
import '../types/constants.dart';
import '../types/types.dart';
import '../universe/call_structure.dart';
import '../universe/selector.dart';
import '../universe/side_effects.dart';
import '../util/util.dart';
import '../world.dart';
import 'closure_tracer.dart';
import 'debug.dart' as debug;
import 'locals_handler.dart';
import 'list_tracer.dart';
import 'map_tracer.dart';
import 'builder.dart';
import 'builder_kernel.dart';
import 'type_graph_dump.dart';
import 'type_graph_inferrer.dart';
import 'type_graph_nodes.dart';
import 'type_system.dart';
@ -40,7 +37,7 @@ import 'type_system.dart';
/// An inferencing engine that computes a call graph of [TypeInformation] nodes
/// by visiting the AST of the application, and then does the inferencing on the
/// graph.
abstract class InferrerEngine {
abstract class InferrerEngine<T> {
/// A set of selector names that [List] implements, that we know return their
/// element type.
final Set<Selector> returnsListElementTypeSet =
@ -64,8 +61,8 @@ abstract class InferrerEngine {
CommonMasks get commonMasks => closedWorld.commonMasks;
CommonElements get commonElements => closedWorld.commonElements;
TypeSystem<ast.Node> get types;
Map<ast.Node, TypeInformation> get concreteTypes;
TypeSystem<T> get types;
Map<T, TypeInformation> get concreteTypes;
/// Parallel structure for concreteTypes.
// TODO(efortuna): Remove concreteTypes and/or parameterize InferrerEngine by
@ -76,7 +73,7 @@ abstract class InferrerEngine {
void runOverAllElements();
void analyze(ResolvedAst resolvedAst, ArgumentsTypes arguments);
void analyze(MemberEntity member, T node, ArgumentsTypes arguments);
void analyzeListAndEnqueue(ListTypeInformation info);
void analyzeMapAndEnqueue(MapTypeInformation info);
@ -136,11 +133,11 @@ abstract class InferrerEngine {
/// Registers a call to await with an expression of type [argumentType] as
/// argument.
TypeInformation registerAwait(ast.Node node, TypeInformation argument);
TypeInformation registerAwait(T node, TypeInformation argument);
/// Registers a call to yield with an expression of type [argumentType] as
/// argument.
TypeInformation registerYield(ast.Node node, TypeInformation argument);
TypeInformation registerYield(T node, TypeInformation argument);
/// Registers that [caller] calls [closure] with [arguments].
///
@ -149,7 +146,7 @@ abstract class InferrerEngine {
///
/// [inLoop] tells whether the call happens in a loop.
TypeInformation registerCalledClosure(
ast.Node node,
T node,
Selector selector,
TypeMask mask,
TypeInformation closure,
@ -184,7 +181,7 @@ abstract class InferrerEngine {
/// [inLoop] tells whether the call happens in a loop.
TypeInformation registerCalledSelector(
CallType callType,
ast.Node node,
T node,
Selector selector,
TypeMask mask,
TypeInformation receiverType,
@ -201,8 +198,8 @@ abstract class InferrerEngine {
ArgumentsTypes arguments, Selector selector, TypeMask mask,
{bool remove, bool addToQueue: true});
void updateSelectorInMember(MemberEntity owner, CallType callType,
ast.Node node, Selector selector, TypeMask mask);
void updateSelectorInMember(MemberEntity owner, CallType callType, T node,
Selector selector, TypeMask mask);
/// Returns the return type of [element].
TypeInformation returnTypeOfMember(MemberEntity element);
@ -228,7 +225,7 @@ abstract class InferrerEngine {
void clear();
}
class InferrerEngineImpl extends InferrerEngine {
abstract class InferrerEngineImpl<T> extends InferrerEngine<T> {
final Map<Local, TypeInformation> defaultTypeOfParameter =
new Map<Local, TypeInformation>();
final WorkQueue workQueue = new WorkQueue();
@ -249,9 +246,8 @@ class InferrerEngineImpl extends InferrerEngine {
final ClosedWorld closedWorld;
final ClosedWorldRefiner closedWorldRefiner;
final TypeSystem<ast.Node> types;
final Map<ast.Node, TypeInformation> concreteTypes =
new Map<ast.Node, TypeInformation>();
final TypeSystem<T> types;
final Map<T, TypeInformation> concreteTypes = new Map<T, TypeInformation>();
final Map<ir.Node, TypeInformation> concreteKernelTypes =
new Map<ir.Node, TypeInformation>();
@ -263,10 +259,13 @@ class InferrerEngineImpl extends InferrerEngine {
final Map<MemberEntity, GlobalTypeInferenceElementData> _memberData =
new Map<MemberEntity, GlobalTypeInferenceElementData>();
InferrerEngineImpl(this.compiler, ClosedWorld closedWorld,
this.closedWorldRefiner, this.mainElement)
: this.types = new TypeSystem<ast.Node>(
closedWorld, const TypeSystemStrategyImpl()),
InferrerEngineImpl(
this.compiler,
ClosedWorld closedWorld,
this.closedWorldRefiner,
this.mainElement,
TypeSystemStrategy<T> typeSystemStrategy)
: this.types = new TypeSystem<T>(closedWorld, typeSystemStrategy),
this.closedWorld = closedWorld;
void forEachElementMatching(
@ -277,12 +276,13 @@ class InferrerEngineImpl extends InferrerEngine {
}
}
// TODO(johnniwinther): Make this private again.
GlobalTypeInferenceElementData dataOfMember(MemberEntity element) =>
_memberData.putIfAbsent(
element, () => new GlobalTypeInferenceElementData());
GlobalTypeInferenceElementData<T> createElementData();
GlobalTypeInferenceElementData lookupDataOfMember(MemberEntity element) =>
// TODO(johnniwinther): Make this private again.
GlobalTypeInferenceElementData<T> dataOfMember(MemberEntity element) =>
_memberData.putIfAbsent(element, createElementData);
GlobalTypeInferenceElementData<T> lookupDataOfMember(MemberEntity element) =>
_memberData[element];
/**
@ -358,8 +358,8 @@ class InferrerEngineImpl extends InferrerEngine {
return returnType;
}
void updateSelectorInMember(MemberEntity owner, CallType callType,
ast.Node node, Selector selector, TypeMask mask) {
void updateSelectorInMember(MemberEntity owner, CallType callType, T node,
Selector selector, TypeMask mask) {
GlobalTypeInferenceElementData data = dataOfMember(owner);
assert(validCallType(callType, node));
switch (callType) {
@ -456,280 +456,9 @@ class InferrerEngineImpl extends InferrerEngine {
workQueue.add(info);
}
void runOverAllElements() {
if (compiler.disableTypeInference) return;
if (compiler.options.verbose) {
compiler.progress.reset();
}
sortResolvedAsts().forEach((ResolvedAst resolvedAst) {
if (compiler.shouldPrintProgress) {
reporter.log('Added $addedInGraph elements in inferencing graph.');
compiler.progress.reset();
}
// This also forces the creation of the [ElementTypeInformation] to ensure
// it is in the graph.
MemberElement member = resolvedAst.element;
types.withMember(member, () => analyze(resolvedAst, null));
});
reporter.log('Added $addedInGraph elements in inferencing graph.');
void runOverAllElements();
TypeGraphDump dump = debug.PRINT_GRAPH ? new TypeGraphDump(this) : null;
dump?.beforeAnalysis();
buildWorkQueue();
refine();
// Try to infer element types of lists and compute their escape information.
types.allocatedLists.values.forEach((TypeInformation info) {
analyzeListAndEnqueue(info);
});
// Try to infer the key and value types for maps and compute the values'
// escape information.
types.allocatedMaps.values.forEach((TypeInformation info) {
analyzeMapAndEnqueue(info);
});
Set<FunctionEntity> bailedOutOn = new Set<FunctionEntity>();
// Trace closures to potentially infer argument types.
types.allocatedClosures.forEach((dynamic info) {
void trace(
Iterable<FunctionEntity> elements, ClosureTracerVisitor tracer) {
tracer.run();
if (!tracer.continueAnalyzing) {
elements.forEach((FunctionEntity _element) {
MethodElement element = _element;
MethodElement implementation = element.implementation;
closedWorldRefiner.registerMightBePassedToApply(element);
if (debug.VERBOSE) {
print("traced closure $element as ${true} (bail)");
}
implementation.functionSignature
.forEachParameter((FormalElement _parameter) {
ParameterElement parameter = _parameter;
types
.getInferredTypeOfParameter(parameter)
.giveUp(this, clearAssignments: false);
});
});
bailedOutOn.addAll(elements);
return;
}
elements
.where((e) => !bailedOutOn.contains(e))
.forEach((FunctionEntity _element) {
MethodElement element = _element;
MethodElement implementation = element.implementation;
implementation.functionSignature
.forEachParameter((FormalElement _parameter) {
ParameterElement parameter = _parameter;
ParameterTypeInformation info =
types.getInferredTypeOfParameter(parameter);
info.maybeResume();
workQueue.add(info);
});
if (tracer.tracedType.mightBePassedToFunctionApply) {
closedWorldRefiner.registerMightBePassedToApply(element);
}
if (debug.VERBOSE) {
print("traced closure $element as "
"${closedWorldRefiner
.getCurrentlyKnownMightBePassedToApply(element)}");
}
});
}
if (info is ClosureTypeInformation) {
Iterable<FunctionEntity> elements = [info.closure];
trace(elements, new ClosureTracerVisitor(elements, info, this));
} else if (info is CallSiteTypeInformation) {
if (info is StaticCallSiteTypeInformation &&
info.selector != null &&
info.selector.isCall) {
// This is a constructor call to a class with a call method. So we
// need to trace the call method here.
MethodElement calledElement = info.calledElement;
assert(calledElement.isGenerativeConstructor);
ClassElement cls = calledElement.enclosingClass;
MethodElement callMethod = cls.lookupMember(Identifiers.call);
if (callMethod == null) {
callMethod = cls.lookupMember(Identifiers.noSuchMethod_);
}
assert(callMethod != null, failedAt(cls));
Iterable<FunctionEntity> elements = [callMethod];
trace(elements, new ClosureTracerVisitor(elements, info, this));
} else {
// We only are interested in functions here, as other targets
// of this closure call are not a root to trace but an intermediate
// for some other function.
Iterable<FunctionEntity> elements = new List<FunctionEntity>.from(
info.callees.where((e) => e.isFunction));
trace(elements, new ClosureTracerVisitor(elements, info, this));
}
} else if (info is MemberTypeInformation) {
trace(<FunctionEntity>[info.member],
new StaticTearOffClosureTracerVisitor(info.member, info, this));
} else if (info is ParameterTypeInformation) {
failedAt(
NO_LOCATION_SPANNABLE, 'Unexpected closure allocation info $info');
}
});
dump?.beforeTracing();
// Reset all nodes that use lists/maps that have been inferred, as well
// as nodes that use elements fetched from these lists/maps. The
// workset for a new run of the analysis will be these nodes.
Set<TypeInformation> seenTypes = new Set<TypeInformation>();
while (!workQueue.isEmpty) {
TypeInformation info = workQueue.remove();
if (seenTypes.contains(info)) continue;
// If the node cannot be reset, we do not need to update its users either.
if (!info.reset(this)) continue;
seenTypes.add(info);
workQueue.addAll(info.users);
}
workQueue.addAll(seenTypes);
refine();
if (debug.PRINT_SUMMARY) {
types.allocatedLists.values.forEach((_info) {
ListTypeInformation info = _info;
print('${info.type} '
'for ${info.originalType.allocationNode} '
'at ${info.originalType.allocationElement} '
'after ${info.refineCount}');
});
types.allocatedMaps.values.forEach((_info) {
MapTypeInformation info = _info;
print('${info.type} '
'for ${info.originalType.allocationNode} '
'at ${info.originalType.allocationElement} '
'after ${info.refineCount}');
});
types.allocatedClosures.forEach((TypeInformation info) {
if (info is ElementTypeInformation) {
print('${info.getInferredSignature(types)} for '
'${info.debugName}');
} else if (info is ClosureTypeInformation) {
print('${info.getInferredSignature(types)} for '
'${info.debugName}');
} else if (info is DynamicCallSiteTypeInformation) {
for (MemberEntity target in info.targets) {
if (target is FunctionEntity) {
print(
'${types.getInferredSignatureOfMethod(target)} for ${target}');
} else {
print(
'${types.getInferredTypeOfMember(target).type} for ${target}');
}
}
} else if (info is StaticCallSiteTypeInformation) {
ClassElement cls = info.calledElement.enclosingClass;
MethodElement callMethod = cls.lookupMember(Identifiers.call);
print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}');
} else {
print('${info.type} for some unknown kind of closure');
}
});
analyzedElements.forEach((MemberEntity elem) {
TypeInformation type = types.getInferredTypeOfMember(elem);
print('${elem} :: ${type} from ${type.assignments} ');
});
}
dump?.afterAnalysis();
reporter.log('Inferred $overallRefineCount types.');
processLoopInformation();
}
void analyze(ResolvedAst resolvedAst, ArgumentsTypes arguments) {
MemberElement element = resolvedAst.element;
if (analyzedElements.contains(element)) return;
analyzedElements.add(element);
dynamic visitor = compiler.options.kernelGlobalInference
? new KernelTypeGraphBuilder(element, resolvedAst, compiler, this)
: new ElementGraphBuilder(element, resolvedAst, compiler, this);
TypeInformation type;
reporter.withCurrentElement(element, () {
// ignore: UNDEFINED_METHOD
type = visitor.run();
});
addedInGraph++;
if (element.isField) {
FieldElement field = element;
ast.Node initializer = resolvedAst.body;
if (field.isFinal || field.isConst) {
// If [element] is final and has an initializer, we record
// the inferred type.
if (resolvedAst.body != null) {
if (type is! ListTypeInformation && type is! MapTypeInformation) {
// For non-container types, the constant handler does
// constant folding that could give more precise results.
ConstantExpression constant = field.constant;
if (constant != null) {
ConstantValue value =
compiler.backend.constants.getConstantValue(constant);
if (value != null) {
if (value.isFunction) {
FunctionConstantValue functionConstant = value;
MethodElement function = functionConstant.element;
type = types.allocateClosure(function);
} else {
// Although we might find a better type, we have to keep
// the old type around to ensure that we get a complete view
// of the type graph and do not drop any flow edges.
TypeMask refinedType = computeTypeMask(closedWorld, value);
assert(TypeMask.assertIsNormalized(refinedType, closedWorld));
type = new NarrowTypeInformation(type, refinedType);
types.allocatedTypes.add(type);
}
} else {
assert(
field.isInstanceMember ||
constant.isImplicit ||
constant.isPotential,
failedAt(
field,
"Constant expression without value: "
"${constant.toStructuredText()}."));
}
}
}
recordTypeOfField(field, type);
} else if (!element.isInstanceMember) {
recordTypeOfField(field, types.nullType);
}
} else if (initializer == null) {
// Only update types of static fields if there is no
// assignment. Instance fields are dealt with in the constructor.
if (Elements.isStaticOrTopLevelField(element)) {
recordTypeOfField(field, type);
}
} else {
recordTypeOfField(field, type);
}
if (Elements.isStaticOrTopLevelField(field) &&
resolvedAst.body != null &&
!element.isConst) {
dynamic argument = resolvedAst.body;
// TODO(13429): We could do better here by using the
// constant handler to figure out if it's a lazy field or not.
if (argument.asSend() != null ||
(argument.asNewExpression() != null && !argument.isConst)) {
recordTypeOfField(field, types.nullType);
}
}
} else {
MethodElement method = element;
recordReturnType(method, type);
}
}
void analyze(MemberEntity element, T body, ArgumentsTypes arguments);
void processLoopInformation() {
types.allocatedCalls.forEach((dynamic info) {
@ -789,77 +518,7 @@ class InferrerEngineImpl extends InferrerEngine {
void updateParameterAssignments(TypeInformation caller, MemberEntity callee,
ArgumentsTypes arguments, Selector selector, TypeMask mask,
{bool remove, bool addToQueue: true}) {
if (callee.name == Identifiers.noSuchMethod_) return;
if (callee.isField) {
if (selector.isSetter) {
ElementTypeInformation info = types.getInferredTypeOfMember(callee);
if (remove) {
info.removeAssignment(arguments.positional[0]);
} else {
info.addAssignment(arguments.positional[0]);
}
if (addToQueue) workQueue.add(info);
}
} else if (callee.isGetter) {
return;
} else if (selector != null && selector.isGetter) {
// We are tearing a function off and thus create a closure.
assert(callee.isFunction);
MethodElement method = callee;
MemberTypeInformation info = types.getInferredTypeOfMember(method);
if (remove) {
info.closurizedCount--;
} else {
info.closurizedCount++;
if (Elements.isStaticOrTopLevel(method)) {
types.allocatedClosures.add(info);
} else {
// We add the call-site type information here so that we
// can benefit from further refinement of the selector.
types.allocatedClosures.add(caller);
}
FunctionElement function = method.implementation;
FunctionSignature signature = function.functionSignature;
signature.forEachParameter((FormalElement _parameter) {
ParameterElement parameter = _parameter;
ParameterTypeInformation info =
types.getInferredTypeOfParameter(parameter);
info.tagAsTearOffClosureParameter(this);
if (addToQueue) workQueue.add(info);
});
}
} else {
MethodElement method = callee;
FunctionElement function = method.implementation;
FunctionSignature signature = function.functionSignature;
int parameterIndex = 0;
bool visitingRequiredParameter = true;
signature.forEachParameter((FormalElement _parameter) {
ParameterElement parameter = _parameter;
if (signature.hasOptionalParameters &&
parameter == signature.optionalParameters.first) {
visitingRequiredParameter = false;
}
TypeInformation type = visitingRequiredParameter
? arguments.positional[parameterIndex]
: signature.optionalParametersAreNamed
? arguments.named[parameter.name]
: parameterIndex < arguments.positional.length
? arguments.positional[parameterIndex]
: null;
if (type == null) type = getDefaultTypeOfParameter(parameter);
TypeInformation info = types.getInferredTypeOfParameter(parameter);
if (remove) {
info.removeAssignment(type);
} else {
info.addAssignment(type);
}
parameterIndex++;
if (addToQueue) workQueue.add(info);
});
}
}
{bool remove, bool addToQueue: true});
void setDefaultTypeOfParameter(Local parameter, TypeInformation type,
{bool isInstanceMember}) {
@ -982,7 +641,7 @@ class InferrerEngineImpl extends InferrerEngine {
TypeInformation registerCalledSelector(
CallType callType,
ast.Node node,
T node,
Selector selector,
TypeMask mask,
TypeInformation receiverType,
@ -1017,24 +676,24 @@ class InferrerEngineImpl extends InferrerEngine {
return info;
}
TypeInformation registerAwait(ast.Node node, TypeInformation argument) {
TypeInformation registerAwait(T node, TypeInformation argument) {
AwaitTypeInformation info =
new AwaitTypeInformation<ast.Node>(types.currentMember, node);
new AwaitTypeInformation<T>(types.currentMember, node);
info.addAssignment(argument);
types.allocatedTypes.add(info);
return info;
}
TypeInformation registerYield(ast.Node node, TypeInformation argument) {
TypeInformation registerYield(T node, TypeInformation argument) {
YieldTypeInformation info =
new YieldTypeInformation<ast.Node>(types.currentMember, node);
new YieldTypeInformation<T>(types.currentMember, node);
info.addAssignment(argument);
types.allocatedTypes.add(info);
return info;
}
TypeInformation registerCalledClosure(
ast.Node node,
T node,
Selector selector,
TypeMask mask,
TypeInformation closure,
@ -1058,46 +717,6 @@ class InferrerEngineImpl extends InferrerEngine {
return info;
}
// Sorts the resolved elements by size. We do this for this inferrer
// to get the same results for [ListTracer] compared to the
// [SimpleTypesInferrer].
Iterable<ResolvedAst> sortResolvedAsts() {
int max = 0;
Map<int, Setlet<ResolvedAst>> methodSizes = <int, Setlet<ResolvedAst>>{};
compiler.enqueuer.resolution.processedEntities.forEach((_element) {
MemberElement element = _element;
ResolvedAst resolvedAst = element.resolvedAst;
element = element.implementation;
if (element.impliesType) return;
assert(
element.isField ||
element.isFunction ||
element.isConstructor ||
element.isGetter ||
element.isSetter,
failedAt(element, 'Unexpected element kind: ${element.kind}'));
if (element.isAbstract) return;
// Put the other operators in buckets by length, later to be added in
// length order.
int length = 0;
if (resolvedAst.kind == ResolvedAstKind.PARSED) {
TreeElementMapping mapping = resolvedAst.elements;
length = mapping.getSelectorCount();
}
max = length > max ? length : max;
Setlet<ResolvedAst> set =
methodSizes.putIfAbsent(length, () => new Setlet<ResolvedAst>());
set.add(resolvedAst);
});
List<ResolvedAst> result = <ResolvedAst>[];
for (int i = 0; i <= max; i++) {
Setlet<ResolvedAst> set = methodSizes[i];
if (set != null) result.addAll(set);
}
return result;
}
void clear() {
void cleanup(TypeInformation info) => info.cleanup();
@ -1162,104 +781,3 @@ class InferrerEngineImpl extends InferrerEngine {
}
}
}
class TypeSystemStrategyImpl implements TypeSystemStrategy<ast.Node> {
const TypeSystemStrategyImpl();
@override
MemberTypeInformation createMemberTypeInformation(
covariant MemberElement member) {
assert(member.isDeclaration, failedAt(member));
if (member.isField) {
FieldElement field = member;
return new FieldTypeInformation(field, field.type);
} else if (member.isGetter) {
GetterElement getter = member;
return new GetterTypeInformation(getter, getter.type);
} else if (member.isSetter) {
SetterElement setter = member;
return new SetterTypeInformation(setter);
} else if (member.isFunction) {
MethodElement method = member;
return new MethodTypeInformation(method, method.type);
} else {
ConstructorElement constructor = member;
if (constructor.isFactoryConstructor) {
return new FactoryConstructorTypeInformation(
constructor, constructor.type);
} else {
return new GenerativeConstructorTypeInformation(constructor);
}
}
}
@override
ParameterTypeInformation createParameterTypeInformation(
covariant ParameterElement parameter, TypeSystem<ast.Node> types) {
assert(parameter.isImplementation, failedAt(parameter));
FunctionTypedElement function = parameter.functionDeclaration.declaration;
if (function.isLocal) {
LocalFunctionElement localFunction = function;
MethodElement callMethod = localFunction.callMethod;
return new ParameterTypeInformation.localFunction(
types.getInferredTypeOfMember(callMethod),
parameter,
parameter.type,
callMethod);
} else if (function.isInstanceMember) {
MethodElement method = function;
return new ParameterTypeInformation.instanceMember(
types.getInferredTypeOfMember(method),
parameter,
parameter.type,
method,
new ParameterAssignments());
} else {
MethodElement method = function;
return new ParameterTypeInformation.static(
types.getInferredTypeOfMember(method),
parameter,
parameter.type,
method,
// TODO(johnniwinther): Is this still valid now that initializing
// formals also introduce locals?
isInitializingFormal: parameter.isInitializingFormal);
}
}
@override
void forEachParameter(
covariant MethodElement function, void f(Local parameter)) {
MethodElement impl = function.implementation;
FunctionSignature signature = impl.functionSignature;
signature.forEachParameter((FormalElement _parameter) {
ParameterElement parameter = _parameter;
f(parameter);
});
}
@override
bool checkMapNode(ast.Node node) {
return node is ast.LiteralMap;
}
@override
bool checkListNode(ast.Node node) {
return node is ast.LiteralList || node is ast.Send;
}
@override
bool checkLoopPhiNode(ast.Node node) {
return node is ast.Loop || node is ast.SwitchStatement;
}
@override
bool checkPhiNode(ast.Node node) {
return true;
}
@override
bool checkClassEntity(covariant ClassElement cls) {
return cls.isDeclaration;
}
}

View file

@ -15,6 +15,7 @@ import '../types/masks.dart'
import '../types/types.dart' show TypesInferrer;
import '../universe/selector.dart' show Selector;
import '../world.dart' show ClosedWorld, ClosedWorldRefiner;
import 'ast_inferrer_engine.dart';
import 'inferrer_engine.dart';
import 'type_graph_nodes.dart';
@ -65,7 +66,7 @@ class TypeGraphInferrer implements TypesInferrer {
void analyzeMain(FunctionEntity main) {
inferrer =
new InferrerEngineImpl(compiler, closedWorld, closedWorldRefiner, main);
new AstInferrerEngine(compiler, closedWorld, closedWorldRefiner, main);
inferrer.runOverAllElements();
}

View file

@ -831,7 +831,7 @@ enum CallType {
forIn,
}
bool validCallType(CallType callType, Spannable call) {
bool validCallType(CallType callType, Object call) {
switch (callType) {
case CallType.complex:
return call is ast.SendSet;
@ -855,7 +855,7 @@ bool validCallType(CallType callType, Spannable call) {
*/
abstract class CallSiteTypeInformation extends TypeInformation
with ApplyableTypeInformation {
final Spannable _call;
final Object _call;
final MemberEntity caller;
final Selector selector;
final TypeMask mask;
@ -966,7 +966,7 @@ class StaticCallSiteTypeInformation extends CallSiteTypeInformation {
}
}
class DynamicCallSiteTypeInformation extends CallSiteTypeInformation {
class DynamicCallSiteTypeInformation<T> extends CallSiteTypeInformation {
final CallType _callType;
final TypeInformation receiver;
final bool isConditional;
@ -977,7 +977,7 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation {
DynamicCallSiteTypeInformation(
MemberTypeInformation context,
this._callType,
ast.Node call,
T call,
MemberEntity enclosing,
Selector selector,
TypeMask mask,
@ -1296,7 +1296,7 @@ class ClosureCallSiteTypeInformation extends CallSiteTypeInformation {
ClosureCallSiteTypeInformation(
MemberTypeInformation context,
Spannable call,
Object call,
MemberEntity enclosing,
Selector selector,
TypeMask mask,

View file

@ -145,7 +145,32 @@ class GlobalTypeInferenceParameterResult
/// Internal data used during type-inference to store intermediate results about
/// a single element.
class GlobalTypeInferenceElementData {
abstract class GlobalTypeInferenceElementData<T> {
TypeMask typeOfSend(T node);
TypeMask typeOfGetter(T node);
TypeMask typeOfOperator(T node);
void setTypeMask(T node, TypeMask mask);
void setGetterTypeMaskInComplexSendSet(T node, TypeMask mask);
void setOperatorTypeMaskInComplexSendSet(T node, TypeMask mask);
TypeMask typeOfIterator(T node);
TypeMask typeOfIteratorMoveNext(T node);
TypeMask typeOfIteratorCurrent(T node);
void setIteratorTypeMask(T node, TypeMask mask);
void setMoveNextTypeMask(T node, TypeMask mask);
void setCurrentTypeMask(T node, TypeMask mask);
}
class AstGlobalTypeInferenceElementData
extends GlobalTypeInferenceElementData<Node> {
Map<Object, TypeMask> _typeMasks;
TypeMask _get(Object node) => _typeMasks != null ? _typeMasks[node] : null;
@ -154,19 +179,22 @@ class GlobalTypeInferenceElementData {
_typeMasks[node] = mask;
}
TypeMask typeOfSend(Send node) => _get(node);
TypeMask typeOfGetter(SendSet node) => _get(node.selector);
TypeMask typeOfOperator(SendSet node) => _get(node.assignmentOperator);
TypeMask typeOfSend(covariant Send node) => _get(node);
TypeMask typeOfGetter(covariant SendSet node) => _get(node.selector);
TypeMask typeOfOperator(covariant SendSet node) =>
_get(node.assignmentOperator);
void setTypeMask(Send node, TypeMask mask) {
void setTypeMask(covariant Send node, TypeMask mask) {
_set(node, mask);
}
void setGetterTypeMaskInComplexSendSet(SendSet node, TypeMask mask) {
void setGetterTypeMaskInComplexSendSet(
covariant SendSet node, TypeMask mask) {
_set(node.selector, mask);
}
void setOperatorTypeMaskInComplexSendSet(SendSet node, TypeMask mask) {
void setOperatorTypeMaskInComplexSendSet(
covariant SendSet node, TypeMask mask) {
_set(node.assignmentOperator, mask);
}
@ -176,21 +204,21 @@ class GlobalTypeInferenceElementData {
// separate. The current implementation does this by using
// children of the for-in node (these children were picked arbitrarily).
TypeMask typeOfIterator(ForIn node) => _get(node);
TypeMask typeOfIterator(covariant ForIn node) => _get(node);
TypeMask typeOfIteratorMoveNext(ForIn node) => _get(node.forToken);
TypeMask typeOfIteratorMoveNext(covariant ForIn node) => _get(node.forToken);
TypeMask typeOfIteratorCurrent(ForIn node) => _get(node.inToken);
TypeMask typeOfIteratorCurrent(covariant ForIn node) => _get(node.inToken);
void setIteratorTypeMask(ForIn node, TypeMask mask) {
void setIteratorTypeMask(covariant ForIn node, TypeMask mask) {
_set(node, mask);
}
void setMoveNextTypeMask(ForIn node, TypeMask mask) {
void setMoveNextTypeMask(covariant ForIn node, TypeMask mask) {
_set(node.forToken, mask);
}
void setCurrentTypeMask(ForIn node, TypeMask mask) {
void setCurrentTypeMask(covariant ForIn node, TypeMask mask) {
_set(node.inToken, mask);
}
}