Chromium Code Reviews| 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); |
| + } |
| + } |
| } |