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:kernel/ast.dart' show DartType; | 6 import 'package:front_end/src/dependency_walker.dart' as dependencyWalker; |
7 import 'package:kernel/ast.dart' show DartType, DynamicType, Member; | |
7 import 'package:kernel/class_hierarchy.dart'; | 8 import 'package:kernel/class_hierarchy.dart'; |
8 import 'package:kernel/core_types.dart'; | 9 import 'package:kernel/core_types.dart'; |
9 | 10 |
11 /// Data structure for tracking dependencies between fields that require type | |
12 /// inference. | |
13 /// | |
14 /// TODO(paulberry): see if it's possible to make this class more lightweight | |
15 /// by changing the API so that the walker is passed to computeDependencies(). | |
16 /// (This should allow us to drop the _typeInferrer field). | |
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 | |
10 /// Abstract implementation of type inference which is independent of the | 35 /// Abstract implementation of type inference which is independent of the |
11 /// underlying AST representation (but still uses DartType from kernel). | 36 /// underlying AST representation (but still uses DartType from kernel). |
12 /// | 37 /// |
13 /// TODO(paulberry): would it make more sense to abstract away the | 38 /// TODO(paulberry): would it make more sense to abstract away the |
14 /// representation of types as well? | 39 /// representation of types as well? |
15 /// | 40 /// |
16 /// Derived classes should set S, E, V, and F to the class they use to represent | 41 /// Derived classes should set S, E, V, and F to the class they use to represent |
17 /// statements, expressions, variable declarations, and field declarations, | 42 /// statements, expressions, variable declarations, and field declarations, |
18 /// respectively. | 43 /// respectively. |
19 abstract class TypeInferrer<S, E, V, F> { | 44 abstract class TypeInferrer<S, E, V, F> { |
20 final CoreTypes coreTypes; | |
21 | |
22 final ClassHierarchy classHierarchy; | |
23 | |
24 final Instrumentation instrumentation; | 45 final Instrumentation instrumentation; |
25 | 46 |
26 final bool strongMode; | 47 final bool strongMode; |
27 | 48 |
49 final fieldNodes = <FieldNode<F>>[]; | |
50 | |
51 CoreTypes coreTypes; | |
52 | |
53 ClassHierarchy classHierarchy; | |
54 | |
28 /// The URI of the code for which type inference is currently being | 55 /// The URI of the code for which type inference is currently being |
29 /// performed--this is used for testing. | 56 /// performed--this is used for testing. |
30 Uri uri; | 57 String uri; |
31 | 58 |
32 TypeInferrer(this.coreTypes, this.classHierarchy, this.instrumentation, | 59 /// Indicates whether we are currently performing top level inference. |
33 this.strongMode); | 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 `nul` if the type is | |
karlklose
2017/04/21 11:11:01
'nul' -> 'null'.
Paul Berry
2017/04/21 11:32:25
Done.
| |
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 | |
78 /// Gets the initializer for the given [field], or `null` if there is no | |
79 /// initializer. | |
80 E getFieldInitializer(F field); | |
81 | |
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); | |
34 | 91 |
35 /// Performs type inference on a method with the given method [body]. | 92 /// Performs type inference on a method with the given method [body]. |
36 /// | 93 /// |
37 /// [uri] is the URI of the file the method is contained in--this is used for | 94 /// [uri] is the URI of the file the method is contained in--this is used for |
38 /// testing. | 95 /// testing. |
39 void inferBody(S body, Uri uri) { | 96 void inferBody(S body, Uri uri) { |
40 this.uri = uri; | 97 this.uri = uri.toString(); |
41 inferStatement(body); | 98 inferStatement(body); |
42 } | 99 } |
43 | 100 |
44 /// Performs type inference on the given [expression]. | 101 /// Performs type inference on the given [expression]. |
45 /// | 102 /// |
46 /// [typeContext] is the expected type of the expression, based on surrounding | 103 /// [typeContext] is the expected type of the expression, based on surrounding |
47 /// code. [typeNeeded] indicates whether it is necessary to compute the | 104 /// code. [typeNeeded] indicates whether it is necessary to compute the |
48 /// actual type of the expression. If [typeNeeded] is `true`, the actual type | 105 /// actual type of the expression. If [typeNeeded] is `true`, the actual type |
49 /// of the expression is returned; otherwise `null` is returned. | 106 /// of the expression is returned; otherwise `null` is returned. |
50 /// | 107 /// |
51 /// Derived classes should override this method with logic that dispatches on | 108 /// Derived classes should override this method with logic that dispatches on |
52 /// the expression type and calls the appropriate specialized "infer" method. | 109 /// the expression type and calls the appropriate specialized "infer" method. |
53 DartType inferExpression(E expression, DartType typeContext, bool typeNeeded); | 110 DartType inferExpression(E expression, DartType typeContext, bool typeNeeded); |
54 | 111 |
112 /// Performs type inference on the given [field]. | |
113 void inferField(F field) { | |
114 var initializer = getFieldInitializer(field); | |
115 if (initializer != null) { | |
116 var type = getFieldDeclaredType(field); | |
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): the following is a hack so that outlines don't contain | |
129 // initializers. But it means that we rebuild the initializers when doing | |
130 // a full compile. There should be a better way. | |
131 clearFieldInitializer(field); | |
132 } | |
133 } | |
134 | |
135 /// Makes a note that the given [field] is part of a circularity, so its type | |
136 /// can't be inferred. | |
137 void inferFieldCircular(F field) { | |
138 // TODO(paulberry): report the appropriate error. | |
139 if (getFieldDeclaredType(field) == null) { | |
140 var uri = getFieldUri(field); | |
141 instrumentation?.record('topType', Uri.parse(uri), getFieldOffset(field), | |
142 const InstrumentationValueLiteral('circular')); | |
143 setFieldInferredType(field, const DynamicType()); | |
144 } | |
karlklose
2017/04/21 11:11:01
Add an TODO to do type-checking.
Paul Berry
2017/04/21 11:32:25
Done. (Actually the comment belongs in inferField
| |
145 } | |
146 | |
55 /// Performs the core type inference algorithm for integer literals. | 147 /// Performs the core type inference algorithm for integer literals. |
56 /// | 148 /// |
57 /// [typeContext], [typeNeeded], and the return value behave as described in | 149 /// [typeContext], [typeNeeded], and the return value behave as described in |
58 /// [inferExpression]. | 150 /// [inferExpression]. |
59 DartType inferIntLiteral(DartType typeContext, bool typeNeeded) { | 151 DartType inferIntLiteral(DartType typeContext, bool typeNeeded) { |
60 return typeNeeded ? coreTypes.intClass.rawType : null; | 152 return typeNeeded ? coreTypes.intClass.rawType : null; |
61 } | 153 } |
62 | 154 |
63 /// Performs type inference on the given [statement]. | 155 /// Performs type inference on the given [statement]. |
64 /// | 156 /// |
65 /// Derived classes should override this method with logic that dispatches on | 157 /// Derived classes should override this method with logic that dispatches on |
66 /// the statement type and calls the appropriate specialized "infer" method. | 158 /// the statement type and calls the appropriate specialized "infer" method. |
67 void inferStatement(S statement); | 159 void inferStatement(S statement); |
68 | 160 |
161 /// Performs the core type inference algorithm for static variable getters. | |
162 /// | |
163 /// [typeContext], [typeNeeded], and the return value behave as described in | |
164 /// [inferExpression]. | |
165 /// | |
166 /// [getterType] is the type of the field being referenced, or the return type | |
167 /// of the getter. | |
168 DartType inferStaticGet( | |
169 DartType typeContext, bool typeNeeded, DartType getterType) { | |
170 return typeNeeded ? getterType : null; | |
171 } | |
172 | |
69 /// Performs the core type inference algorithm for variable declarations. | 173 /// Performs the core type inference algorithm for variable declarations. |
70 /// | 174 /// |
71 /// [declaredType] is the declared type of the variable, or `null` if the type | 175 /// [declaredType] is the declared type of the variable, or `null` if the type |
72 /// should be inferred. [initializer] is the initializer expression. | 176 /// should be inferred. [initializer] is the initializer expression. |
73 /// [offset] is the character offset of the variable declaration (for | 177 /// [offset] is the character offset of the variable declaration (for |
74 /// instrumentation). [setType] is a callback that will be used to set the | 178 /// instrumentation). [setType] is a callback that will be used to set the |
75 /// inferred type. | 179 /// inferred type. |
76 void inferVariableDeclaration(DartType declaredType, E initializer, | 180 void inferVariableDeclaration(DartType declaredType, E initializer, |
77 int offset, void setType(DartType type)) { | 181 int offset, void setType(DartType type)) { |
78 if (initializer == null) return; | 182 if (initializer == null) return; |
79 var inferredType = | 183 var inferredType = |
80 inferExpression(initializer, declaredType, declaredType == null); | 184 inferExpression(initializer, declaredType, declaredType == null); |
81 if (strongMode && declaredType == null) { | 185 if (strongMode && declaredType == null) { |
82 instrumentation?.record( | 186 instrumentation?.record('type', Uri.parse(uri), offset, |
83 'type', uri, offset, new InstrumentationValueForType(inferredType)); | 187 new InstrumentationValueForType(inferredType)); |
84 setType(inferredType); | 188 setType(inferredType); |
85 } | 189 } |
86 } | 190 } |
191 | |
192 /// Determines if the given [field] has completed top level type inference | |
karlklose
2017/04/21 11:11:01
Rephrase it as 'Determines if top level type infer
Paul Berry
2017/04/21 11:32:26
Done.
| |
193 /// yet. | |
194 bool isFieldInferred(F field); | |
195 | |
196 /// Performs top level type inference for all fields that have been passed to | |
197 /// [recordField]. | |
198 void performInitializerInference() { | |
199 for (var fieldNode in fieldNodes) { | |
200 if (fieldNode.isEvaluated) continue; | |
201 new _FieldWalker<F>().walk(fieldNode); | |
202 } | |
203 } | |
204 | |
205 /// Records that the given [field] will need top level type inference. | |
206 void recordField(F field) { | |
207 fieldNodes.add(createFieldNode(field)); | |
208 } | |
209 | |
210 /// Stores [inferredType] as the inferred type of [field]. | |
211 void setFieldInferredType(F field, DartType inferredType); | |
87 } | 212 } |
213 | |
214 /// Subtype of [dependencyWalker.DependencyWalker] which is specialized to | |
215 /// perform top level type inference. | |
216 class _FieldWalker<F> extends dependencyWalker.DependencyWalker<FieldNode<F>> { | |
217 _FieldWalker(); | |
218 | |
219 @override | |
220 void evaluate(FieldNode<F> f) { | |
221 f._typeInferrer.inferField(f._field); | |
222 } | |
223 | |
224 @override | |
225 void evaluateScc(List<FieldNode<F>> scc) { | |
226 for (var f in scc) { | |
227 f._typeInferrer.inferFieldCircular(f._field); | |
228 } | |
229 for (var f in scc) { | |
230 f._typeInferrer.inferField(f._field); | |
231 } | |
232 } | |
233 } | |
OLD | NEW |