Chromium Code Reviews| 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 |