Index: pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart |
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart |
index b7fc20688edb94a371bcabb89d8b83f0fb15c2a6..70a4d54ba8a01e432c3c5de594a4e2a533b21e97 100644 |
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart |
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart |
@@ -3,10 +3,35 @@ |
// BSD-style license that can be found in the LICENSE.md file. |
import 'package:front_end/src/base/instrumentation.dart'; |
-import 'package:kernel/ast.dart' show DartType; |
+import 'package:front_end/src/dependency_walker.dart' as dependencyWalker; |
+import 'package:kernel/ast.dart' show DartType, DynamicType, Member; |
import 'package:kernel/class_hierarchy.dart'; |
import 'package:kernel/core_types.dart'; |
+/// Data structure for tracking dependencies between fields that require type |
+/// inference. |
+/// |
+/// TODO(paulberry): see if it's possible to make this class more lightweight |
+/// by changing the API so that the walker is passed to computeDependencies(). |
+/// (This should allow us to drop the _typeInferrer field). |
+class FieldNode<F> extends dependencyWalker.Node<FieldNode<F>> { |
+ final TypeInferrer _typeInferrer; |
+ |
+ final F _field; |
+ |
+ final dependencies = <FieldNode<F>>[]; |
+ |
+ FieldNode(this._typeInferrer, this._field); |
+ |
+ @override |
+ bool get isEvaluated => _typeInferrer.isFieldInferred(_field); |
+ |
+ @override |
+ List<FieldNode<F>> computeDependencies() { |
+ return dependencies; |
+ } |
+} |
+ |
/// Abstract implementation of type inference which is independent of the |
/// underlying AST representation (but still uses DartType from kernel). |
/// |
@@ -17,27 +42,59 @@ import 'package:kernel/core_types.dart'; |
/// statements, expressions, variable declarations, and field declarations, |
/// respectively. |
abstract class TypeInferrer<S, E, V, F> { |
- final CoreTypes coreTypes; |
- |
- final ClassHierarchy classHierarchy; |
- |
final Instrumentation instrumentation; |
final bool strongMode; |
+ final fieldNodes = <FieldNode<F>>[]; |
+ |
+ CoreTypes coreTypes; |
+ |
+ ClassHierarchy classHierarchy; |
+ |
/// The URI of the code for which type inference is currently being |
/// performed--this is used for testing. |
- Uri uri; |
+ String uri; |
+ |
+ /// Indicates whether we are currently performing top level inference. |
+ bool isTopLevel = false; |
+ |
+ TypeInferrer(this.instrumentation, this.strongMode); |
+ |
+ /// Cleares the initializer of [field]. |
+ void clearFieldInitializer(F field); |
+ |
+ /// Creates a [FieldNode] to track dependencies of the given [field]. |
+ FieldNode<F> createFieldNode(F field); |
+ |
+ /// 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.
|
+ /// implicit. |
+ DartType getFieldDeclaredType(F field); |
- TypeInferrer(this.coreTypes, this.classHierarchy, this.instrumentation, |
- this.strongMode); |
+ /// Gets the list of top level type inference dependencies of the given |
+ /// [field]. |
+ List<FieldNode<F>> getFieldDependencies(F field); |
+ |
+ /// Gets the initializer for the given [field], or `null` if there is no |
+ /// initializer. |
+ E getFieldInitializer(F field); |
+ |
+ /// Gets the [FieldNode] corresponding to the given [readTarget], if any. |
+ FieldNode<F> getFieldNodeForReadTarget(Member readTarget); |
+ |
+ /// Gets the character offset of the declaration of [field] within its |
+ /// compilation unit. |
+ int getFieldOffset(F field); |
+ |
+ /// Gets the URI of the compilation unit the [field] is declared in. |
+ String getFieldUri(F field); |
/// Performs type inference on a method with the given method [body]. |
/// |
/// [uri] is the URI of the file the method is contained in--this is used for |
/// testing. |
void inferBody(S body, Uri uri) { |
- this.uri = uri; |
+ this.uri = uri.toString(); |
inferStatement(body); |
} |
@@ -52,6 +109,41 @@ abstract class TypeInferrer<S, E, V, F> { |
/// the expression type and calls the appropriate specialized "infer" method. |
DartType inferExpression(E expression, DartType typeContext, bool typeNeeded); |
+ /// Performs type inference on the given [field]. |
+ void inferField(F field) { |
+ var initializer = getFieldInitializer(field); |
+ if (initializer != null) { |
+ var type = getFieldDeclaredType(field); |
+ uri = getFieldUri(field); |
+ isTopLevel = true; |
+ var inferredType = inferExpression(initializer, type, type == null); |
+ if (type == null && strongMode) { |
+ instrumentation?.record( |
+ 'topType', |
+ Uri.parse(uri), |
+ getFieldOffset(field), |
+ new InstrumentationValueForType(inferredType)); |
+ setFieldInferredType(field, inferredType); |
+ } |
+ // TODO(paulberry): the following is a hack so that outlines don't contain |
+ // initializers. But it means that we rebuild the initializers when doing |
+ // a full compile. There should be a better way. |
+ clearFieldInitializer(field); |
+ } |
+ } |
+ |
+ /// Makes a note that the given [field] is part of a circularity, so its type |
+ /// can't be inferred. |
+ void inferFieldCircular(F field) { |
+ // TODO(paulberry): report the appropriate error. |
+ if (getFieldDeclaredType(field) == null) { |
+ var uri = getFieldUri(field); |
+ instrumentation?.record('topType', Uri.parse(uri), getFieldOffset(field), |
+ const InstrumentationValueLiteral('circular')); |
+ setFieldInferredType(field, const DynamicType()); |
+ } |
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
|
+ } |
+ |
/// Performs the core type inference algorithm for integer literals. |
/// |
/// [typeContext], [typeNeeded], and the return value behave as described in |
@@ -66,6 +158,18 @@ abstract class TypeInferrer<S, E, V, F> { |
/// the statement type and calls the appropriate specialized "infer" method. |
void inferStatement(S statement); |
+ /// Performs the core type inference algorithm for static variable getters. |
+ /// |
+ /// [typeContext], [typeNeeded], and the return value behave as described in |
+ /// [inferExpression]. |
+ /// |
+ /// [getterType] is the type of the field being referenced, or the return type |
+ /// of the getter. |
+ DartType inferStaticGet( |
+ DartType typeContext, bool typeNeeded, DartType getterType) { |
+ return typeNeeded ? getterType : null; |
+ } |
+ |
/// Performs the core type inference algorithm for variable declarations. |
/// |
/// [declaredType] is the declared type of the variable, or `null` if the type |
@@ -79,9 +183,51 @@ abstract class TypeInferrer<S, E, V, F> { |
var inferredType = |
inferExpression(initializer, declaredType, declaredType == null); |
if (strongMode && declaredType == null) { |
- instrumentation?.record( |
- 'type', uri, offset, new InstrumentationValueForType(inferredType)); |
+ instrumentation?.record('type', Uri.parse(uri), offset, |
+ new InstrumentationValueForType(inferredType)); |
setType(inferredType); |
} |
} |
+ |
+ /// 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.
|
+ /// yet. |
+ bool isFieldInferred(F field); |
+ |
+ /// Performs top level type inference for all fields that have been passed to |
+ /// [recordField]. |
+ void performInitializerInference() { |
+ for (var fieldNode in fieldNodes) { |
+ if (fieldNode.isEvaluated) continue; |
+ new _FieldWalker<F>().walk(fieldNode); |
+ } |
+ } |
+ |
+ /// Records that the given [field] will need top level type inference. |
+ void recordField(F field) { |
+ fieldNodes.add(createFieldNode(field)); |
+ } |
+ |
+ /// Stores [inferredType] as the inferred type of [field]. |
+ void setFieldInferredType(F field, DartType inferredType); |
+} |
+ |
+/// Subtype of [dependencyWalker.DependencyWalker] which is specialized to |
+/// perform top level type inference. |
+class _FieldWalker<F> extends dependencyWalker.DependencyWalker<FieldNode<F>> { |
+ _FieldWalker(); |
+ |
+ @override |
+ void evaluate(FieldNode<F> f) { |
+ f._typeInferrer.inferField(f._field); |
+ } |
+ |
+ @override |
+ void evaluateScc(List<FieldNode<F>> scc) { |
+ for (var f in scc) { |
+ f._typeInferrer.inferFieldCircular(f._field); |
+ } |
+ for (var f in scc) { |
+ f._typeInferrer.inferField(f._field); |
+ } |
+ } |
} |