| Index: pkg/compiler/lib/src/inferrer/ast_inferrer_engine.dart
|
| diff --git a/pkg/compiler/lib/src/inferrer/ast_inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/ast_inferrer_engine.dart
|
| index 66e380b04d09e249bff4e6b45562a6655dfd758d..0c0e24217f0b4ec0fdea8ff548c7fb7c54729a8e 100644
|
| --- a/pkg/compiler/lib/src/inferrer/ast_inferrer_engine.dart
|
| +++ b/pkg/compiler/lib/src/inferrer/ast_inferrer_engine.dart
|
| @@ -11,18 +11,12 @@ 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';
|
|
|
| @@ -35,11 +29,7 @@ class AstInferrerEngine extends InferrerEngineImpl<ast.Node> {
|
| GlobalTypeInferenceElementData<ast.Node> createElementData() =>
|
| new AstGlobalTypeInferenceElementData();
|
|
|
| - void runOverAllElements() {
|
| - if (compiler.disableTypeInference) return;
|
| - if (compiler.options.verbose) {
|
| - compiler.progress.reset();
|
| - }
|
| + void analyzeAllElements() {
|
| sortResolvedAsts().forEach((ResolvedAst resolvedAst) {
|
| if (compiler.shouldPrintProgress) {
|
| reporter.log('Added $addedInGraph elements in inferencing graph.');
|
| @@ -55,336 +45,62 @@ class AstInferrerEngine extends InferrerEngineImpl<ast.Node> {
|
| 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');
|
| - }
|
| + void forEachParameter(
|
| + covariant MethodElement method, void f(Local parameter)) {
|
| + MethodElement implementation = method.implementation;
|
| + implementation.functionSignature
|
| + .forEachParameter((FormalElement _parameter) {
|
| + ParameterElement parameter = _parameter;
|
| + f(parameter);
|
| });
|
| + }
|
|
|
| - 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} ');
|
| - });
|
| + FunctionEntity lookupCallMethod(covariant ClassElement cls) {
|
| + MethodElement callMethod = cls.lookupMember(Identifiers.call);
|
| + if (callMethod == null) {
|
| + callMethod = cls.lookupMember(Identifiers.noSuchMethod_);
|
| }
|
| - dump?.afterAnalysis();
|
| -
|
| - reporter.log('Inferred $overallRefineCount types.');
|
| -
|
| - processLoopInformation();
|
| + return callMethod;
|
| }
|
|
|
| - void analyze(MemberEntity element, ast.Node body, ArgumentsTypes arguments) {
|
| - assert(!(element is MemberElement && !element.isDeclaration));
|
| - if (analyzedElements.contains(element)) return;
|
| - analyzedElements.add(element);
|
| -
|
| + TypeInformation computeMemberTypeInformation(
|
| + MemberEntity member, ast.Node body) {
|
| 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++;
|
| + ? new KernelTypeGraphBuilder(member, compiler, this)
|
| + : new ElementGraphBuilder(member, compiler, this);
|
| + // ignore: UNDEFINED_METHOD
|
| + return visitor.run();
|
| + }
|
|
|
| - 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);
|
| - }
|
| + bool isFieldInitializerPotentiallyNull(
|
| + FieldEntity field, ast.Node initializer) {
|
| + dynamic argument = initializer;
|
| + // TODO(13429): We could do better here by using the
|
| + // constant handler to figure out if it's a lazy field or not.
|
| + return argument.asSend() != null ||
|
| + (argument.asNewExpression() != null && !argument.isConst);
|
| }
|
|
|
| - 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);
|
| - });
|
| + ConstantValue getFieldConstant(covariant FieldElement field) {
|
| + ConstantExpression constant = field.constant;
|
| + if (constant != null) {
|
| + ConstantValue value =
|
| + compiler.backend.constants.getConstantValue(constant);
|
| + if (value == null) {
|
| + assert(
|
| + field.isInstanceMember ||
|
| + constant.isImplicit ||
|
| + constant.isPotential,
|
| + failedAt(
|
| + field,
|
| + "Constant expression without value: "
|
| + "${constant.toStructuredText()}."));
|
| }
|
| - } 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);
|
| - });
|
| + return value;
|
| }
|
| + return null;
|
| }
|
|
|
| // Sorts the resolved elements by size. We do this for this inferrer
|
|
|