| 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
|
| index 57fa870701bebba765e895cc4fa800c0772db7b1..b21d5929a211cd9fe5fc54ce1356c1a345dbb4f8 100644
|
| --- 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
|
| @@ -4,29 +4,16 @@
|
|
|
| import 'package:front_end/src/base/instrumentation.dart';
|
| import 'package:front_end/src/dependency_walker.dart' as dependencyWalker;
|
| +import 'package:front_end/src/fasta/errors.dart';
|
| import 'package:front_end/src/fasta/kernel/kernel_shadow_ast.dart';
|
| import 'package:front_end/src/fasta/type_inference/type_inference_listener.dart';
|
| import 'package:front_end/src/fasta/type_inference/type_inferrer.dart';
|
| import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
|
| import 'package:kernel/ast.dart'
|
| - show Class, DartType, DynamicType, InterfaceType;
|
| + show Class, DartType, DynamicType, Field, InterfaceType, Member, Procedure;
|
| import 'package:kernel/class_hierarchy.dart';
|
| import 'package:kernel/core_types.dart';
|
| -
|
| -/// Enum tracking the type inference state of a field.
|
| -enum FieldState {
|
| - /// The field's type has not been inferred yet.
|
| - NotInferredYet,
|
| -
|
| - /// Type inference is in progress for the field.
|
| - ///
|
| - /// This means that code is currently on the stack which is attempting to
|
| - /// determine the type of the field.
|
| - Inferring,
|
| -
|
| - /// The field's type has been inferred.
|
| - Inferred
|
| -}
|
| +import 'package:kernel/type_algebra.dart';
|
|
|
| /// Data structure for tracking dependencies between fields that require type
|
| /// inference.
|
| @@ -50,6 +37,8 @@ class FieldNode extends dependencyWalker.Node<FieldNode> {
|
| /// Otherwise `null`.
|
| FieldNode currentDependency;
|
|
|
| + final overrides = <Member>[];
|
| +
|
| FieldNode(this._typeInferenceEngine, this.field);
|
|
|
| @override
|
| @@ -64,6 +53,21 @@ class FieldNode extends dependencyWalker.Node<FieldNode> {
|
| String toString() => field.toString();
|
| }
|
|
|
| +/// Enum tracking the type inference state of a field.
|
| +enum FieldState {
|
| + /// The field's type has not been inferred yet.
|
| + NotInferredYet,
|
| +
|
| + /// Type inference is in progress for the field.
|
| + ///
|
| + /// This means that code is currently on the stack which is attempting to
|
| + /// determine the type of the field.
|
| + Inferring,
|
| +
|
| + /// The field's type has been inferred.
|
| + Inferred
|
| +}
|
| +
|
| /// Keeps track of the global state for the type inference that occurs outside
|
| /// of method bodies and initalizers.
|
| ///
|
| @@ -155,7 +159,21 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
|
|
|
| /// Computes type inference dependencies for the given [field].
|
| List<FieldNode> computeFieldDependencies(FieldNode fieldNode) {
|
| - // TODO(paulberry): add logic to infer field types by inheritance.
|
| + // If the field's type is going to be determined by inheritance, then its
|
| + // dependencies are determined by inheritance too.
|
| + if (fieldNode.overrides.isNotEmpty) {
|
| + var dependencies = <FieldNode>[];
|
| + for (var override in fieldNode.overrides) {
|
| + // TODO(paulberry): support dependencies on getters/setters too.
|
| + if (override is Field) {
|
| + dependencies.add(KernelField.getFieldNode(override));
|
| + }
|
| + }
|
| + fieldNode.isImmediatelyEvident = true;
|
| + return dependencies;
|
| + }
|
| +
|
| + // Otherwise its dependencies are based on the initializer expression.
|
| if (expandedTopLevelInference) {
|
| // In expanded top level inference, we determine the dependencies by
|
| // doing a "dry run" of top level inference and recording which static
|
| @@ -212,56 +230,29 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
|
| /// a previous call to [createTopLevelTypeInferrer].
|
| TypeInferrerImpl getFieldTypeInferrer(KernelField field);
|
|
|
| - /// Performs fused type inference on the given [field].
|
| - void inferFieldFused(FieldNode fieldNode, FieldNode dependant) {
|
| - switch (fieldNode.state) {
|
| - case FieldState.Inferred:
|
| - // Already inferred. Nothing to do.
|
| - break;
|
| - case FieldState.Inferring:
|
| - // A field depends on itself (possibly by way of intermediate fields).
|
| - // Mark all fields involved as circular and infer a type of `dynamic`
|
| - // for them.
|
| - var node = fieldNode;
|
| - while (node != null) {
|
| - var nextNode = node.currentDependency;
|
| - inferFieldCircular(node);
|
| - node.currentDependency = null;
|
| - node = nextNode;
|
| - }
|
| - break;
|
| - case FieldState.NotInferredYet:
|
| - // Mark the "dependant" field (if any) as depending on this one, and
|
| - // invoke field inference for this node.
|
| - dependant?.currentDependency = fieldNode;
|
| - // All fields are "immediately evident" when doing fused inference.
|
| - fieldNode.isImmediatelyEvident = true;
|
| - inferField(fieldNode);
|
| - dependant?.currentDependency = null;
|
| - break;
|
| - }
|
| - }
|
| -
|
| /// Performs type inference on the given [field].
|
| void inferField(FieldNode fieldNode) {
|
| assert(fieldNode.state == FieldState.NotInferredYet);
|
| fieldNode.state = FieldState.Inferring;
|
| var field = fieldNode.field;
|
| - var typeInferrer = getFieldTypeInferrer(field);
|
| if (strongMode) {
|
| - typeInferrer.isImmediatelyEvident = true;
|
| - var inferredType = fieldNode.isImmediatelyEvident
|
| - ? typeInferrer.inferDeclarationType(
|
| - typeInferrer.inferFieldTopLevel(field, null, true))
|
| - : const DynamicType();
|
| + var inferredType = tryInferFieldByInheritance(fieldNode);
|
| + var typeInferrer = getFieldTypeInferrer(field);
|
| + if (inferredType == null) {
|
| + typeInferrer.isImmediatelyEvident = true;
|
| + inferredType = fieldNode.isImmediatelyEvident
|
| + ? typeInferrer.inferDeclarationType(
|
| + typeInferrer.inferFieldTopLevel(field, null, true))
|
| + : const DynamicType();
|
| + if (!typeInferrer.isImmediatelyEvident) {
|
| + inferredType = const DynamicType();
|
| + }
|
| + }
|
| if (fieldNode.state == FieldState.Inferred) {
|
| // A circularity must have been detected; at the time it was detected,
|
| // inference for this node was completed.
|
| return;
|
| }
|
| - if (!typeInferrer.isImmediatelyEvident) {
|
| - inferredType = const DynamicType();
|
| - }
|
| instrumentation?.record(
|
| Uri.parse(typeInferrer.uri),
|
| getFieldOffset(field),
|
| @@ -291,6 +282,36 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
|
| field.type = inferredType;
|
| }
|
|
|
| + /// Performs fused type inference on the given [field].
|
| + void inferFieldFused(FieldNode fieldNode, FieldNode dependant) {
|
| + switch (fieldNode.state) {
|
| + case FieldState.Inferred:
|
| + // Already inferred. Nothing to do.
|
| + break;
|
| + case FieldState.Inferring:
|
| + // A field depends on itself (possibly by way of intermediate fields).
|
| + // Mark all fields involved as circular and infer a type of `dynamic`
|
| + // for them.
|
| + var node = fieldNode;
|
| + while (node != null) {
|
| + var nextNode = node.currentDependency;
|
| + inferFieldCircular(node);
|
| + node.currentDependency = null;
|
| + node = nextNode;
|
| + }
|
| + break;
|
| + case FieldState.NotInferredYet:
|
| + // Mark the "dependant" field (if any) as depending on this one, and
|
| + // invoke field inference for this node.
|
| + dependant?.currentDependency = fieldNode;
|
| + // All fields are "immediately evident" when doing fused inference.
|
| + fieldNode.isImmediatelyEvident = true;
|
| + inferField(fieldNode);
|
| + dependant?.currentDependency = null;
|
| + break;
|
| + }
|
| + }
|
| +
|
| @override
|
| void prepareTopLevel(CoreTypes coreTypes, ClassHierarchy hierarchy) {
|
| this.coreTypes = coreTypes;
|
| @@ -309,6 +330,54 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
|
| initializingFormals.add(formal);
|
| }
|
|
|
| + DartType tryInferFieldByInheritance(FieldNode fieldNode) {
|
| + DartType inferredType;
|
| + for (var override in fieldNode.overrides) {
|
| + var nextInferredType = _computeOverriddenFieldType(override, fieldNode);
|
| + if (inferredType == null) {
|
| + inferredType = nextInferredType;
|
| + } else if (inferredType != nextInferredType) {
|
| + // Overrides don't have matching types.
|
| + // TODO(paulberry): report an error
|
| + return const DynamicType();
|
| + }
|
| + }
|
| + return inferredType;
|
| + }
|
| +
|
| + DartType _computeOverriddenFieldType(Member override, FieldNode fieldNode) {
|
| + DartType overriddenType;
|
| + if (override is Field) {
|
| + if (fusedTopLevelInference) {
|
| + FieldNode dependency = KernelField.getFieldNode(override);
|
| + if (dependency != null) {
|
| + inferFieldFused(dependency, fieldNode);
|
| + }
|
| + }
|
| + overriddenType = override.type;
|
| + } else if (override is Procedure) {
|
| + // TODO(paulberry): handle the case where override needs its type
|
| + // inferred first.
|
| + if (override.isGetter) {
|
| + overriddenType = override.getterType;
|
| + } else {
|
| + overriddenType = override.setterType;
|
| + }
|
| + } else {
|
| + throw internalError(
|
| + 'Unexpected overridden member type: ${override.runtimeType}');
|
| + }
|
| + var superclass = override.enclosingClass;
|
| + if (superclass.typeParameters.isEmpty) return overriddenType;
|
| + var thisClass = fieldNode.field.enclosingClass;
|
| + var superclassInstantiation = classHierarchy
|
| + .getClassAsInstanceOf(thisClass, superclass)
|
| + .asInterfaceType;
|
| + return Substitution
|
| + .fromInterfaceType(superclassInstantiation)
|
| + .substituteType(overriddenType);
|
| + }
|
| +
|
| DartType _inferInitializingFormalType(KernelVariableDeclaration formal) {
|
| assert(KernelVariableDeclaration.isImplicitlyTyped(formal));
|
| Class enclosingClass = formal.parent.parent.parent;
|
|
|