| Index: pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
|
| diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..91196c868d543ef130858c1038324495e4543140
|
| --- /dev/null
|
| +++ b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
|
| @@ -0,0 +1,207 @@
|
| +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE.md file.
|
| +
|
| +import 'package:front_end/src/base/instrumentation.dart';
|
| +import 'package:front_end/src/dependency_walker.dart' as dependencyWalker;
|
| +import 'package:front_end/src/fasta/type_inference/type_inferrer.dart';
|
| +import 'package:kernel/ast.dart' show DartType, DynamicType;
|
| +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 _typeInferenceEngine field).
|
| +class FieldNode<F> extends dependencyWalker.Node<FieldNode<F>> {
|
| + final TypeInferenceEngineImpl _typeInferenceEngine;
|
| +
|
| + final F _field;
|
| +
|
| + final dependencies = <FieldNode<F>>[];
|
| +
|
| + FieldNode(this._typeInferenceEngine, this._field);
|
| +
|
| + @override
|
| + bool get isEvaluated => _typeInferenceEngine.isFieldInferred(_field);
|
| +
|
| + @override
|
| + List<FieldNode<F>> computeDependencies() {
|
| + return dependencies;
|
| + }
|
| +}
|
| +
|
| +/// Keeps track of the global state for the type inference that occurs outside
|
| +/// of method bodies and initalizers.
|
| +///
|
| +/// This class abstracts away the representation of the underlying AST using
|
| +/// generic parameters. TODO(paulberry): would it make more sense to abstract
|
| +/// away the representation of types as well?
|
| +///
|
| +/// Derived classes should set F to the class they use to represent field
|
| +/// declarations.
|
| +///
|
| +/// This class describes the interface for use by clients of type inference
|
| +/// (e.g. DietListener). Derived classes should derive from
|
| +/// [TypeInferenceEngineImpl].
|
| +abstract class TypeInferenceEngine<F> {
|
| + ClassHierarchy get classHierarchy;
|
| +
|
| + CoreTypes get coreTypes;
|
| +
|
| + /// Creates a type inferrer for use inside of a method body declared in a file
|
| + /// with the given [uri].
|
| + TypeInferrer<dynamic, dynamic, dynamic, F> createLocalTypeInferrer(Uri uri);
|
| +
|
| + /// Creates a [TypeInferrer] object which is ready to perform type inference
|
| + /// on the given [field].
|
| + TypeInferrer<dynamic, dynamic, dynamic, F> createTopLevelTypeInferrer(
|
| + F field);
|
| +
|
| + /// Performs the second phase of top level initializer inference, which is to
|
| + /// visit all fields and top level variables that were passed to [recordField]
|
| + /// in topologically-sorted order and assign their types.
|
| + void finishTopLevel();
|
| +
|
| + /// Gets the list of top level type inference dependencies of the given
|
| + /// [field].
|
| + List<FieldNode<F>> getFieldDependencies(F field);
|
| +
|
| + /// Gets ready to do top level type inference for the program having the given
|
| + /// [hierarchy], using the given [coreTypes].
|
| + void prepareTopLevel(CoreTypes coreTypes, ClassHierarchy hierarchy);
|
| +
|
| + /// Records that the given [field] will need top level type inference.
|
| + void recordField(F field);
|
| +}
|
| +
|
| +/// Derived class containing generic implementations of
|
| +/// [TypeInferenceEngineImpl].
|
| +///
|
| +/// This class contains as much of the implementation of type inference as
|
| +/// possible without knowing the identity of the type parameter. It defers to
|
| +/// abstract methods for everything else.
|
| +abstract class TypeInferenceEngineImpl<F> extends TypeInferenceEngine<F> {
|
| + final Instrumentation instrumentation;
|
| +
|
| + final bool strongMode;
|
| +
|
| + final fieldNodes = <FieldNode<F>>[];
|
| +
|
| + @override
|
| + CoreTypes coreTypes;
|
| +
|
| + @override
|
| + ClassHierarchy classHierarchy;
|
| +
|
| + TypeInferenceEngineImpl(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);
|
| +
|
| + /// Queries whether the given [field] has an initializer.
|
| + bool fieldHasInitializer(F field);
|
| +
|
| + @override
|
| + void finishTopLevel() {
|
| + for (var fieldNode in fieldNodes) {
|
| + if (fieldNode.isEvaluated) continue;
|
| + new _FieldWalker<F>().walk(fieldNode);
|
| + }
|
| + }
|
| +
|
| + /// Gets the declared type of the given [field], or `null` if the type is
|
| + /// implicit.
|
| + DartType getFieldDeclaredType(F field);
|
| +
|
| + /// Gets the character offset of the declaration of [field] within its
|
| + /// compilation unit.
|
| + int getFieldOffset(F field);
|
| +
|
| + /// Retrieve the [TypeInferrer] for the given [field], which was created by
|
| + /// a previous call to [createTopLevelTypeInferrer].
|
| + TypeInferrerImpl<dynamic, dynamic, dynamic, F> getFieldTypeInferrer(F field);
|
| +
|
| + /// Gets the URI of the compilation unit the [field] is declared in.
|
| + /// TODO(paulberry): can we remove this?
|
| + String getFieldUri(F field);
|
| +
|
| + /// Performs type inference on the given [field].
|
| + void inferField(F field) {
|
| + if (fieldHasInitializer(field)) {
|
| + var typeInferrer = getFieldTypeInferrer(field);
|
| + var type = getFieldDeclaredType(field);
|
| + var inferredType =
|
| + typeInferrer.inferFieldInitializer(field, type, type == null);
|
| + if (type == null && strongMode) {
|
| + instrumentation?.record(
|
| + 'topType',
|
| + Uri.parse(typeInferrer.uri),
|
| + getFieldOffset(field),
|
| + new InstrumentationValueForType(inferredType));
|
| + setFieldInferredType(field, inferredType);
|
| + }
|
| + // TODO(paulberry): if type != null, then check that the type of the
|
| + // initializer is assignable to it.
|
| + // 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 = getFieldTypeInferrer(field).uri;
|
| + instrumentation?.record('topType', Uri.parse(uri), getFieldOffset(field),
|
| + const InstrumentationValueLiteral('circular'));
|
| + setFieldInferredType(field, const DynamicType());
|
| + }
|
| + }
|
| +
|
| + /// Determines if top level type inference has been completed for [field].
|
| + bool isFieldInferred(F field);
|
| +
|
| + @override
|
| + void prepareTopLevel(CoreTypes coreTypes, ClassHierarchy hierarchy) {
|
| + this.coreTypes = coreTypes;
|
| + this.classHierarchy = classHierarchy;
|
| + }
|
| +
|
| + @override
|
| + 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._typeInferenceEngine.inferField(f._field);
|
| + }
|
| +
|
| + @override
|
| + void evaluateScc(List<FieldNode<F>> scc) {
|
| + for (var f in scc) {
|
| + f._typeInferenceEngine.inferFieldCircular(f._field);
|
| + }
|
| + for (var f in scc) {
|
| + f._typeInferenceEngine.inferField(f._field);
|
| + }
|
| + }
|
| +}
|
|
|