OLD | NEW |
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE.md file. | 3 // BSD-style license that can be found in the LICENSE.md file. |
4 | 4 |
5 import 'package:front_end/src/base/instrumentation.dart'; | 5 import 'package:front_end/src/base/instrumentation.dart'; |
6 import 'package:front_end/src/dependency_walker.dart' as dependencyWalker; | 6 import 'package:front_end/src/fasta/type_inference/type_inference_engine.dart'; |
7 import 'package:kernel/ast.dart' show DartType, DynamicType, Member; | 7 import 'package:kernel/ast.dart' show DartType, Member; |
8 import 'package:kernel/class_hierarchy.dart'; | |
9 import 'package:kernel/core_types.dart'; | 8 import 'package:kernel/core_types.dart'; |
10 | 9 |
11 /// Data structure for tracking dependencies between fields that require type | 10 /// Keeps track of the local state for the type inference that occurs during |
12 /// inference. | 11 /// compilation of a single method body or top level initializer. |
13 /// | 12 /// |
14 /// TODO(paulberry): see if it's possible to make this class more lightweight | 13 /// This class abstracts away the representation of the underlying AST using |
15 /// by changing the API so that the walker is passed to computeDependencies(). | 14 /// generic parameters. TODO(paulberry): would it make more sense to abstract |
16 /// (This should allow us to drop the _typeInferrer field). | 15 /// away the representation of types as well? |
17 class FieldNode<F> extends dependencyWalker.Node<FieldNode<F>> { | |
18 final TypeInferrer _typeInferrer; | |
19 | |
20 final F _field; | |
21 | |
22 final dependencies = <FieldNode<F>>[]; | |
23 | |
24 FieldNode(this._typeInferrer, this._field); | |
25 | |
26 @override | |
27 bool get isEvaluated => _typeInferrer.isFieldInferred(_field); | |
28 | |
29 @override | |
30 List<FieldNode<F>> computeDependencies() { | |
31 return dependencies; | |
32 } | |
33 } | |
34 | |
35 /// Abstract implementation of type inference which is independent of the | |
36 /// underlying AST representation (but still uses DartType from kernel). | |
37 /// | |
38 /// TODO(paulberry): would it make more sense to abstract away the | |
39 /// representation of types as well? | |
40 /// | 16 /// |
41 /// Derived classes should set S, E, V, and F to the class they use to represent | 17 /// Derived classes should set S, E, V, and F to the class they use to represent |
42 /// statements, expressions, variable declarations, and field declarations, | 18 /// statements, expressions, variable declarations, and field declarations, |
43 /// respectively. | 19 /// respectively. |
| 20 /// |
| 21 /// This class describes the interface for use by clients of type inference |
| 22 /// (e.g. BodyBuilder). Derived classes should derive from [TypeInferrerImpl]. |
44 abstract class TypeInferrer<S, E, V, F> { | 23 abstract class TypeInferrer<S, E, V, F> { |
45 final Instrumentation instrumentation; | 24 /// The URI of the code for which type inference is currently being |
| 25 /// performed--this is used for testing. |
| 26 String get uri; |
| 27 |
| 28 /// Gets the [FieldNode] corresponding to the given [readTarget], if any. |
| 29 FieldNode<F> getFieldNodeForReadTarget(Member readTarget); |
| 30 |
| 31 /// Performs type inference on the given [statement]. |
| 32 /// |
| 33 /// Derived classes should override this method with logic that dispatches on |
| 34 /// the statement type and calls the appropriate specialized "infer" method. |
| 35 void inferStatement(S statement); |
| 36 } |
| 37 |
| 38 /// Derived class containing generic implementations of [TypeInferrerImpl]. |
| 39 /// |
| 40 /// This class contains as much of the implementation of type inference as |
| 41 /// possible without knowing the identity of the type parameters. It defers to |
| 42 /// abstract methods for everything else. |
| 43 abstract class TypeInferrerImpl<S, E, V, F> extends TypeInferrer<S, E, V, F> { |
| 44 @override |
| 45 final String uri; |
| 46 |
| 47 /// Indicates whether the construct we are currently performing inference for |
| 48 /// is outside of a method body, and hence top level type inference rules |
| 49 /// should apply. |
| 50 final bool isTopLevel = false; |
| 51 |
| 52 final CoreTypes coreTypes; |
46 | 53 |
47 final bool strongMode; | 54 final bool strongMode; |
48 | 55 |
49 final fieldNodes = <FieldNode<F>>[]; | 56 final Instrumentation instrumentation; |
50 | 57 |
51 CoreTypes coreTypes; | 58 TypeInferrerImpl(TypeInferenceEngineImpl<F> engine, this.uri) |
52 | 59 : coreTypes = engine.coreTypes, |
53 ClassHierarchy classHierarchy; | 60 strongMode = engine.strongMode, |
54 | 61 instrumentation = engine.instrumentation; |
55 /// The URI of the code for which type inference is currently being | |
56 /// performed--this is used for testing. | |
57 String uri; | |
58 | |
59 /// Indicates whether we are currently performing top level inference. | |
60 bool isTopLevel = false; | |
61 | |
62 TypeInferrer(this.instrumentation, this.strongMode); | |
63 | |
64 /// Cleares the initializer of [field]. | |
65 void clearFieldInitializer(F field); | |
66 | |
67 /// Creates a [FieldNode] to track dependencies of the given [field]. | |
68 FieldNode<F> createFieldNode(F field); | |
69 | |
70 /// Gets the declared type of the given [field], or `null` if the type is | |
71 /// implicit. | |
72 DartType getFieldDeclaredType(F field); | |
73 | |
74 /// Gets the list of top level type inference dependencies of the given | |
75 /// [field]. | |
76 List<FieldNode<F>> getFieldDependencies(F field); | |
77 | 62 |
78 /// Gets the initializer for the given [field], or `null` if there is no | 63 /// Gets the initializer for the given [field], or `null` if there is no |
79 /// initializer. | 64 /// initializer. |
80 E getFieldInitializer(F field); | 65 E getFieldInitializer(F field); |
81 | 66 |
82 /// Gets the [FieldNode] corresponding to the given [readTarget], if any. | |
83 FieldNode<F> getFieldNodeForReadTarget(Member readTarget); | |
84 | |
85 /// Gets the character offset of the declaration of [field] within its | |
86 /// compilation unit. | |
87 int getFieldOffset(F field); | |
88 | |
89 /// Gets the URI of the compilation unit the [field] is declared in. | |
90 String getFieldUri(F field); | |
91 | |
92 /// Performs type inference on a method with the given method [body]. | |
93 /// | |
94 /// [uri] is the URI of the file the method is contained in--this is used for | |
95 /// testing. | |
96 void inferBody(S body, Uri uri) { | |
97 this.uri = uri.toString(); | |
98 inferStatement(body); | |
99 } | |
100 | |
101 /// Performs type inference on the given [expression]. | 67 /// Performs type inference on the given [expression]. |
102 /// | 68 /// |
103 /// [typeContext] is the expected type of the expression, based on surrounding | 69 /// [typeContext] is the expected type of the expression, based on surrounding |
104 /// code. [typeNeeded] indicates whether it is necessary to compute the | 70 /// code. [typeNeeded] indicates whether it is necessary to compute the |
105 /// actual type of the expression. If [typeNeeded] is `true`, the actual type | 71 /// actual type of the expression. If [typeNeeded] is `true`, the actual type |
106 /// of the expression is returned; otherwise `null` is returned. | 72 /// of the expression is returned; otherwise `null` is returned. |
107 /// | 73 /// |
108 /// Derived classes should override this method with logic that dispatches on | 74 /// Derived classes should override this method with logic that dispatches on |
109 /// the expression type and calls the appropriate specialized "infer" method. | 75 /// the expression type and calls the appropriate specialized "infer" method. |
110 DartType inferExpression(E expression, DartType typeContext, bool typeNeeded); | 76 DartType inferExpression(E expression, DartType typeContext, bool typeNeeded); |
111 | 77 |
112 /// Performs type inference on the given [field]. | 78 /// Performs type inference on the given [field]'s initializer expression. |
113 void inferField(F field) { | 79 /// |
114 var initializer = getFieldInitializer(field); | 80 /// Derived classes should provide an implementation that calls |
115 if (initializer != null) { | 81 /// [inferExpression] for the given [field]'s initializer expression. |
116 var type = getFieldDeclaredType(field); | 82 DartType inferFieldInitializer(F field, DartType type, bool typeNeeded); |
117 uri = getFieldUri(field); | |
118 isTopLevel = true; | |
119 var inferredType = inferExpression(initializer, type, type == null); | |
120 if (type == null && strongMode) { | |
121 instrumentation?.record( | |
122 'topType', | |
123 Uri.parse(uri), | |
124 getFieldOffset(field), | |
125 new InstrumentationValueForType(inferredType)); | |
126 setFieldInferredType(field, inferredType); | |
127 } | |
128 // TODO(paulberry): if type != null, then check that the type of the | |
129 // initializer is assignable to it. | |
130 // TODO(paulberry): the following is a hack so that outlines don't contain | |
131 // initializers. But it means that we rebuild the initializers when doing | |
132 // a full compile. There should be a better way. | |
133 clearFieldInitializer(field); | |
134 } | |
135 } | |
136 | |
137 /// Makes a note that the given [field] is part of a circularity, so its type | |
138 /// can't be inferred. | |
139 void inferFieldCircular(F field) { | |
140 // TODO(paulberry): report the appropriate error. | |
141 if (getFieldDeclaredType(field) == null) { | |
142 var uri = getFieldUri(field); | |
143 instrumentation?.record('topType', Uri.parse(uri), getFieldOffset(field), | |
144 const InstrumentationValueLiteral('circular')); | |
145 setFieldInferredType(field, const DynamicType()); | |
146 } | |
147 } | |
148 | 83 |
149 /// Performs the core type inference algorithm for integer literals. | 84 /// Performs the core type inference algorithm for integer literals. |
150 /// | 85 /// |
151 /// [typeContext], [typeNeeded], and the return value behave as described in | 86 /// [typeContext], [typeNeeded], and the return value behave as described in |
152 /// [inferExpression]. | 87 /// [inferExpression]. |
153 DartType inferIntLiteral(DartType typeContext, bool typeNeeded) { | 88 DartType inferIntLiteral(DartType typeContext, bool typeNeeded) { |
154 return typeNeeded ? coreTypes.intClass.rawType : null; | 89 return typeNeeded ? coreTypes.intClass.rawType : null; |
155 } | 90 } |
156 | 91 |
157 /// Performs type inference on the given [statement]. | |
158 /// | |
159 /// Derived classes should override this method with logic that dispatches on | |
160 /// the statement type and calls the appropriate specialized "infer" method. | |
161 void inferStatement(S statement); | |
162 | |
163 /// Performs the core type inference algorithm for static variable getters. | 92 /// Performs the core type inference algorithm for static variable getters. |
164 /// | 93 /// |
165 /// [typeContext], [typeNeeded], and the return value behave as described in | 94 /// [typeContext], [typeNeeded], and the return value behave as described in |
166 /// [inferExpression]. | 95 /// [inferExpression]. |
167 /// | 96 /// |
168 /// [getterType] is the type of the field being referenced, or the return type | 97 /// [getterType] is the type of the field being referenced, or the return type |
169 /// of the getter. | 98 /// of the getter. |
170 DartType inferStaticGet( | 99 DartType inferStaticGet( |
171 DartType typeContext, bool typeNeeded, DartType getterType) { | 100 DartType typeContext, bool typeNeeded, DartType getterType) { |
172 return typeNeeded ? getterType : null; | 101 return typeNeeded ? getterType : null; |
(...skipping 10 matching lines...) Expand all Loading... |
183 int offset, void setType(DartType type)) { | 112 int offset, void setType(DartType type)) { |
184 if (initializer == null) return; | 113 if (initializer == null) return; |
185 var inferredType = | 114 var inferredType = |
186 inferExpression(initializer, declaredType, declaredType == null); | 115 inferExpression(initializer, declaredType, declaredType == null); |
187 if (strongMode && declaredType == null) { | 116 if (strongMode && declaredType == null) { |
188 instrumentation?.record('type', Uri.parse(uri), offset, | 117 instrumentation?.record('type', Uri.parse(uri), offset, |
189 new InstrumentationValueForType(inferredType)); | 118 new InstrumentationValueForType(inferredType)); |
190 setType(inferredType); | 119 setType(inferredType); |
191 } | 120 } |
192 } | 121 } |
193 | |
194 /// Determines if top level type inference has been completed for [field]. | |
195 bool isFieldInferred(F field); | |
196 | |
197 /// Performs top level type inference for all fields that have been passed to | |
198 /// [recordField]. | |
199 void performInitializerInference() { | |
200 for (var fieldNode in fieldNodes) { | |
201 if (fieldNode.isEvaluated) continue; | |
202 new _FieldWalker<F>().walk(fieldNode); | |
203 } | |
204 } | |
205 | |
206 /// Records that the given [field] will need top level type inference. | |
207 void recordField(F field) { | |
208 fieldNodes.add(createFieldNode(field)); | |
209 } | |
210 | |
211 /// Stores [inferredType] as the inferred type of [field]. | |
212 void setFieldInferredType(F field, DartType inferredType); | |
213 } | 122 } |
214 | |
215 /// Subtype of [dependencyWalker.DependencyWalker] which is specialized to | |
216 /// perform top level type inference. | |
217 class _FieldWalker<F> extends dependencyWalker.DependencyWalker<FieldNode<F>> { | |
218 _FieldWalker(); | |
219 | |
220 @override | |
221 void evaluate(FieldNode<F> f) { | |
222 f._typeInferrer.inferField(f._field); | |
223 } | |
224 | |
225 @override | |
226 void evaluateScc(List<FieldNode<F>> scc) { | |
227 for (var f in scc) { | |
228 f._typeInferrer.inferFieldCircular(f._field); | |
229 } | |
230 for (var f in scc) { | |
231 f._typeInferrer.inferField(f._field); | |
232 } | |
233 } | |
234 } | |
OLD | NEW |