| Index: pkg/compiler/lib/src/types/types.dart
|
| diff --git a/pkg/compiler/lib/src/types/types.dart b/pkg/compiler/lib/src/types/types.dart
|
| index ad890dc6c79f6aaaee44a7880f2eb00d1abbeba8..82bed6faeda55b3eb75ed903fd817038a59330fc 100644
|
| --- a/pkg/compiler/lib/src/types/types.dart
|
| +++ b/pkg/compiler/lib/src/types/types.dart
|
| @@ -4,17 +4,165 @@
|
|
|
| library types;
|
|
|
| +import '../closure.dart' show SynthesizedCallMethodElementX;
|
| +import '../common.dart' show invariant;
|
| import '../common/tasks.dart' show CompilerTask;
|
| import '../compiler.dart' show Compiler;
|
| import '../elements/elements.dart';
|
| -import '../inferrer/type_graph_inferrer.dart' show TypeGraphInferrer;
|
| +import '../inferrer/type_graph_inferrer.dart'
|
| + show TypeGraphInferrer, TypeInformationSystem;
|
| import '../tree/tree.dart';
|
| -import '../resolution/tree_elements.dart';
|
| import '../universe/selector.dart' show Selector;
|
| +import '../util/util.dart' show Maplet;
|
|
|
| import 'masks.dart';
|
| export 'masks.dart';
|
|
|
| +/// Results about a single element (e.g. a method, parameter, or field)
|
| +/// produced by the global type-inference algorithm.
|
| +///
|
| +/// All queries in this class may contain results that assume whole-program
|
| +/// closed-world semantics. Any [TypeMask] for an element or node that we return
|
| +/// was inferred to be a "guaranteed type", that means, it is a type that we
|
| +/// can prove to be correct for all executions of the program. A trivial
|
| +/// implementation would return false on all boolean properties (giving no
|
| +/// guarantees) and the `subclass of Object or null` type mask for the type
|
| +/// based queries (the runtime value could be anything).
|
| +abstract class GlobalTypeInferenceElementResult {
|
| + /// Whether the method element associated with this result always throws.
|
| + bool get throwsAlways;
|
| +
|
| + /// Whether the element associated with this result is only called once in one
|
| + /// location in the entire program.
|
| + bool get isCalledOnce;
|
| +
|
| + /// The inferred type when this result belongs to a parameter or field
|
| + /// element, null otherwise.
|
| + TypeMask get type;
|
| +
|
| + /// The inferred return type when this result belongs to a function element.
|
| + TypeMask get returnType;
|
| +
|
| + /// Returns the type of a list allocation [node] (which can be a list
|
| + /// literal or a list new expression).
|
| + TypeMask typeOfNewList(Node node);
|
| +
|
| + /// Returns the type of a send [node].
|
| + TypeMask typeOfSend(Node node);
|
| +
|
| + /// Returns the type of the operator of a complex send-set [node], for
|
| + /// example, the type of `+` in `a += b`.
|
| + TypeMask typeOfGetter(SendSet node);
|
| +
|
| + /// Returns the type of the getter in a complex send-set [node], for example,
|
| + /// the type of the `a.f` getter in `a.f += b`.
|
| + TypeMask typeOfOperator(SendSet node);
|
| +
|
| + /// Returns the type of the iterator in a [loop].
|
| + TypeMask typeOfIterator(ForIn node);
|
| +
|
| + /// Returns the type of the `moveNext` call of an iterator in a [loop].
|
| + TypeMask typeOfIteratorMoveNext(ForIn node);
|
| +
|
| + /// Returns the type of the `current` getter of an iterator in a [loop].
|
| + TypeMask typeOfIteratorCurrent(ForIn node);
|
| +}
|
| +
|
| +class GlobalTypeInferenceElementResultImpl
|
| + implements GlobalTypeInferenceElementResult {
|
| + // TODO(sigmund): delete, store data directly here.
|
| + final Element _owner;
|
| +
|
| + // TODO(sigmund): split - stop using _data after inference is done.
|
| + final GlobalTypeInferenceElementData _data;
|
| +
|
| + // TODO(sigmund): store relevant data & drop reference to inference engine.
|
| + final TypesInferrer _inferrer;
|
| + final bool _isJsInterop;
|
| + final TypeMask _dynamic;
|
| +
|
| + GlobalTypeInferenceElementResultImpl(this._owner, this._data, this._inferrer,
|
| + this._isJsInterop, this._dynamic);
|
| +
|
| + bool get isCalledOnce => _inferrer.isCalledOnce(_owner);
|
| +
|
| + TypeMask get returnType =>
|
| + _isJsInterop ? _dynamic : _inferrer.getReturnTypeOfElement(_owner);
|
| +
|
| + TypeMask get type =>
|
| + _isJsInterop ? _dynamic : _inferrer.getTypeOfElement(_owner);
|
| +
|
| + bool get throwsAlways {
|
| + TypeMask mask = this.returnType;
|
| + // Always throws if the return type was inferred to be non-null empty.
|
| + return mask != null && mask.isEmpty;
|
| + }
|
| +
|
| + TypeMask typeOfNewList(Node node) =>
|
| + _inferrer.getTypeForNewList(_owner, node);
|
| +
|
| + TypeMask typeOfSend(Node node) => _data?.typeOfSend(node);
|
| + TypeMask typeOfGetter(SendSet node) => _data?.typeOfGetter(node);
|
| + TypeMask typeOfOperator(SendSet node) => _data?.typeOfOperator(node);
|
| + TypeMask typeOfIterator(ForIn node) => _data?.typeOfIterator(node);
|
| + TypeMask typeOfIteratorMoveNext(ForIn node) =>
|
| + _data?.typeOfIteratorMoveNext(node);
|
| + TypeMask typeOfIteratorCurrent(ForIn node) =>
|
| + _data?.typeOfIteratorCurrent(node);
|
| +}
|
| +
|
| +/// Internal data used during type-inference to store intermediate results about
|
| +/// a single element.
|
| +class GlobalTypeInferenceElementData {
|
| + Map<Object, TypeMask> _typeMasks;
|
| +
|
| + TypeMask _get(Object node) => _typeMasks != null ? _typeMasks[node] : null;
|
| + void _set(Object node, TypeMask mask) {
|
| + _typeMasks ??= new Maplet<Object, TypeMask>();
|
| + _typeMasks[node] = mask;
|
| + }
|
| +
|
| + TypeMask typeOfSend(Node node) => _get(node);
|
| + TypeMask typeOfGetter(SendSet node) => _get(node.selector);
|
| + TypeMask typeOfOperator(SendSet node) => _get(node.assignmentOperator);
|
| +
|
| + void setTypeMask(Node node, TypeMask mask) {
|
| + _set(node, mask);
|
| + }
|
| +
|
| + void setGetterTypeMaskInComplexSendSet(SendSet node, TypeMask mask) {
|
| + _set(node.selector, mask);
|
| + }
|
| +
|
| + void setOperatorTypeMaskInComplexSendSet(SendSet node, TypeMask mask) {
|
| + _set(node.assignmentOperator, mask);
|
| + }
|
| +
|
| + // TODO(sigmund): clean up. We store data about 3 selectors for "for in"
|
| + // nodes: the iterator, move-next, and current element. Because our map keys
|
| + // are nodes, we need to fabricate different keys to keep these selectors
|
| + // 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 typeOfIteratorMoveNext(ForIn node) => _get(node.forToken);
|
| +
|
| + TypeMask typeOfIteratorCurrent(ForIn node) => _get(node.inToken);
|
| +
|
| + void setIteratorTypeMask(ForIn node, TypeMask mask) {
|
| + _set(node, mask);
|
| + }
|
| +
|
| + void setMoveNextTypeMask(ForIn node, TypeMask mask) {
|
| + _set(node.forToken, mask);
|
| + }
|
| +
|
| + void setCurrentTypeMask(ForIn node, TypeMask mask) {
|
| + _set(node.inToken, mask);
|
| + }
|
| +}
|
| +
|
| /// API to interact with the global type-inference engine.
|
| abstract class TypesInferrer {
|
| void analyzeMain(Element element);
|
| @@ -35,91 +183,48 @@ abstract class TypesInferrer {
|
| /// can prove to be correct for all executions of the program.
|
| class GlobalTypeInferenceResults {
|
| // TODO(sigmund): store relevant data & drop reference to inference engine.
|
| - final TypesInferrer _inferrer;
|
| + final TypeGraphInferrer _inferrer;
|
| final Compiler compiler;
|
| final TypeMask dynamicType;
|
| + final Map<Element, GlobalTypeInferenceElementResult> _elementResults = {};
|
|
|
| - GlobalTypeInferenceResults(this._inferrer, this.compiler, CommonMasks masks)
|
| - : dynamicType = masks.dynamicType;
|
| + // TODO(sigmund,johnniwinther): compute result objects eagerly and make it an
|
| + // error to query for results that don't exist.
|
| + GlobalTypeInferenceElementResult resultOf(AstElement element) {
|
| + assert(invariant(element, !element.isGenerativeConstructorBody,
|
| + message: "unexpected input: ConstructorBodyElements are created"
|
| + " after global type inference, no data is avaiable for them."));
|
|
|
| - /// Returns the type of a parameter or field [element], if any.
|
| - TypeMask typeOf(Element element) {
|
| - // TODO(24489): trust some JsInterop types.
|
| - if (compiler.backend.isJsInterop(element)) return dynamicType;
|
| - return _inferrer.getTypeOfElement(element);
|
| + // TODO(sigmund): store closure data directly in the closure element and
|
| + // not in the context of the enclosing method?
|
| + var key = (element is SynthesizedCallMethodElementX)
|
| + ? element.memberContext
|
| + : element;
|
| + return _elementResults.putIfAbsent(
|
| + element,
|
| + () => new GlobalTypeInferenceElementResultImpl(
|
| + element,
|
| + _inferrer.inferrer.inTreeData[key],
|
| + _inferrer,
|
| + compiler.backend.isJsInterop(element),
|
| + dynamicType));
|
| }
|
|
|
| - /// Returns the return type of a method or function [element].
|
| - TypeMask returnTypeOf(Element element) {
|
| - // TODO(24489): trust some JsInterop types.
|
| - if (compiler.backend.isJsInterop(element)) return dynamicType;
|
| - return _inferrer.getReturnTypeOfElement(element);
|
| - }
|
| + GlobalTypeInferenceResults(this._inferrer, this.compiler, CommonMasks masks,
|
| + TypeInformationSystem types)
|
| + : dynamicType = masks.dynamicType;
|
|
|
| /// Returns the type of a [selector] when applied to a receiver with the given
|
| /// type [mask].
|
| TypeMask typeOfSelector(Selector selector, TypeMask mask) =>
|
| _inferrer.getTypeOfSelector(selector, mask);
|
|
|
| - /// Returns whether the method [element] always throws.
|
| - bool throwsAlways(Element element) {
|
| - // We know the element always throws if the return type was inferred to be
|
| - // non-null empty.
|
| - TypeMask returnType = returnTypeOf(element);
|
| - return returnType != null && returnType.isEmpty;
|
| - }
|
| -
|
| - /// Returns whether [element] is only called once in the entire program.
|
| - bool isCalledOnce(Element element) => _inferrer.isCalledOnce(element);
|
| -
|
| - // TODO(sigmund): introduce a new class [ElementInferenceResult] that can be
|
| - // used to collect any information that is specific about the body within a
|
| - // single element (basically everything below this line). We could consider
|
| - // moving some of the functions above too, for example:
|
| - // results.throwsAlways(element)
|
| - // could become:
|
| - // results[element].alwaysThrows;
|
| - //
|
| -
|
| - /// Returns the type of a list allocation [node] occuring within [owner].
|
| - ///
|
| - /// [node] can be a list literal or a list new expression.
|
| - TypeMask typeOfNewList(Element owner, Node node) =>
|
| - _inferrer.getTypeForNewList(owner, node);
|
| -
|
| /// Returns whether a fixed-length constructor call goes through a growable
|
| /// check.
|
| + // TODO(sigmund): move into the result of the element containing such
|
| + // constructor call.
|
| bool isFixedArrayCheckedForGrowable(Node ctorCall) =>
|
| _inferrer.isFixedArrayCheckedForGrowable(ctorCall);
|
| -
|
| - /// Returns the type of a send [node].
|
| - TypeMask typeOfSend(
|
| - Node node,
|
| - // TODO(sigmund): move inference data out of TreeElements
|
| - TreeElements elements) =>
|
| - elements.getTypeMask(node);
|
| -
|
| - /// Returns the type of the operator of a complex send-set [node], for
|
| - /// example, the type of `+` in `a += b`.
|
| - TypeMask typeOfOperator(Send node, TreeElements elements) =>
|
| - elements.getOperatorTypeMaskInComplexSendSet(node);
|
| -
|
| - /// Returns the type of the getter in a complex send-set [node], for example,
|
| - /// the type of the `a.f` getter in `a.f += b`.
|
| - TypeMask typeOfGetter(node, elements) =>
|
| - elements.getGetterTypeMaskInComplexSendSet(node);
|
| -
|
| - /// Returns the type of the iterator in a [loop].
|
| - TypeMask typeOfIterator(ForIn loop, elements) =>
|
| - elements.getIteratorTypeMask(loop);
|
| -
|
| - /// Returns the type of the `moveNext` call of an iterator in a [loop].
|
| - TypeMask typeOfIteratorMoveNext(ForIn loop, elements) =>
|
| - elements.getMoveNextTypeMask(loop);
|
| -
|
| - /// Returns the type of the `current` getter of an iterator in a [loop].
|
| - TypeMask typeOfIteratorCurrent(ForIn node, elements) =>
|
| - elements.getCurrentTypeMask(node);
|
| }
|
|
|
| /// Global analysis that infers concrete types.
|
| @@ -128,7 +233,7 @@ class GlobalTypeInferenceTask extends CompilerTask {
|
| final String name = 'Type inference';
|
|
|
| final Compiler compiler;
|
| - TypesInferrer typesInferrer;
|
| + TypeGraphInferrer typesInferrer;
|
| CommonMasks masks;
|
| GlobalTypeInferenceResults results;
|
|
|
| @@ -144,7 +249,8 @@ class GlobalTypeInferenceTask extends CompilerTask {
|
| measure(() {
|
| typesInferrer.analyzeMain(mainElement);
|
| typesInferrer.clear();
|
| - results = new GlobalTypeInferenceResults(typesInferrer, compiler, masks);
|
| + results = new GlobalTypeInferenceResults(
|
| + typesInferrer, compiler, masks, typesInferrer.inferrer.types);
|
| });
|
| }
|
| }
|
|
|