Chromium Code Reviews| 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..f55ec95bfbe3201ab99d08a127033d4c3a985df4 100644 |
| --- a/pkg/compiler/lib/src/types/types.dart |
| +++ b/pkg/compiler/lib/src/types/types.dart |
| @@ -7,20 +7,165 @@ library types; |
| 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. |
|
Johnni Winther
2016/11/03 08:50:21
Add `Thus the trivial inference will return false
Siggi Cherem (dart-lang)
2016/11/03 21:12:15
Done.
|
| +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 { |
|
Siggi Cherem (dart-lang)
2016/11/03 01:03:38
I expect this will move under the inferrer, but I'
|
| + 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); |
| TypeMask getReturnTypeOfElement(Element element); |
| TypeMask getTypeOfElement(Element element); |
| TypeMask getTypeForNewList(Element owner, Node node); |
| + |
|
Johnni Winther
2016/11/03 08:50:21
Remove extra line?
Siggi Cherem (dart-lang)
2016/11/03 21:12:15
Done.
|
| TypeMask getTypeOfSelector(Selector selector, TypeMask mask); |
| void clear(); |
| bool isCalledOnce(Element element); |
| @@ -35,91 +180,37 @@ 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; |
| - |
| - GlobalTypeInferenceResults(this._inferrer, this.compiler, CommonMasks masks) |
| - : dynamicType = masks.dynamicType; |
| - |
| - /// 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); |
| + final Map<Element, GlobalTypeInferenceElementResult> _elementResults = {}; |
| + |
| + GlobalTypeInferenceElementResult resultOf(AstElement element) { |
|
Johnni Winther
2016/11/03 08:50:21
Add a TODO to (at some point in the future) fail i
Siggi Cherem (dart-lang)
2016/11/03 21:12:15
Done.
|
| + return _elementResults.putIfAbsent( |
| + element, |
| + () => new GlobalTypeInferenceElementResultImpl( |
| + element, |
| + _inferrer.inferrer.inTreeData[element], |
| + _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 +219,7 @@ class GlobalTypeInferenceTask extends CompilerTask { |
| final String name = 'Type inference'; |
| final Compiler compiler; |
| - TypesInferrer typesInferrer; |
| + TypeGraphInferrer typesInferrer; |
| CommonMasks masks; |
| GlobalTypeInferenceResults results; |
| @@ -144,7 +235,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); |
| }); |
| } |
| } |