Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(386)

Unified Diff: pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart

Issue 2946273002: Implement override-based type inference for instance methods. (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 530bce4262adc8f0448fabce45d0da716a171c07..bf0d4ef6a13151e80353e626fe14e950886fe447 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
@@ -10,7 +10,17 @@ 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, Field, InterfaceType, Member, Procedure;
+ show
+ Class,
+ DartType,
+ DynamicType,
+ Field,
+ FunctionType,
+ InterfaceType,
+ Member,
+ Procedure,
+ TypeParameter,
+ TypeParameterType;
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/type_algebra.dart';
@@ -28,9 +38,9 @@ class AccessorNode extends dependencyWalker.Node<AccessorNode> {
bool isImmediatelyEvident = false;
- AccessorState state = AccessorState.NotInferredYet;
+ InferenceState state = InferenceState.NotInferredYet;
- /// If [state] is [AccessorState.Inferring], and type inference for this
+ /// If [state] is [InferenceState.Inferring], and type inference for this
/// accessor is waiting on type inference of some other accessor, the accessor
/// that is being waited on.
///
@@ -46,7 +56,7 @@ class AccessorNode extends dependencyWalker.Node<AccessorNode> {
get candidateOverrides => overrides.isNotEmpty ? overrides : crossOverrides;
@override
- bool get isEvaluated => state == AccessorState.Inferred;
+ bool get isEvaluated => state == InferenceState.Inferred;
@override
List<AccessorNode> computeDependencies() {
@@ -57,21 +67,36 @@ class AccessorNode extends dependencyWalker.Node<AccessorNode> {
String toString() => member.toString();
}
-/// Enum tracking the type inference state of an accessor.
-enum AccessorState {
- /// The accessor's type has not been inferred yet.
+/// Enum tracking the type inference state of an accessor or method.
+enum InferenceState {
+ /// The accessor or method's type has not been inferred yet.
NotInferredYet,
- /// Type inference is in progress for the accessor.
+ /// Type inference is in progress for the accessor or method.
///
/// This means that code is currently on the stack which is attempting to
- /// determine the type of the accessor.
+ /// determine the type of the accessor or method.
Inferring,
- /// The accessor's type has been inferred.
+ /// The accessor or method's type has been inferred.
Inferred
}
+/// Data structure for tracking dependencies among methods that require type
+/// inference.
+class MethodNode {
+ final KernelProcedure procedure;
+
+ InferenceState state = InferenceState.NotInferredYet;
+
+ final overrides = <Procedure>[];
+
+ MethodNode(this.procedure);
+
+ @override
+ String toString() => procedure.toString();
+}
+
/// Keeps track of the global state for the type inference that occurs outside
/// of method bodies and initalizers.
///
@@ -146,6 +171,8 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
final accessorNodes = <AccessorNode>[];
+ final methodNodes = <MethodNode>[];
+
final initializingFormals = <KernelVariableDeclaration>[];
@override
@@ -213,8 +240,14 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
/// Creates an [AccessorNode] to track dependencies of the given [member].
AccessorNode createAccessorNode(KernelMember member);
+ /// Creates a [MethodNode] to track dependencies of the given [procedure].
+ MethodNode createMethodNode(KernelProcedure procedure);
+
@override
void finishTopLevel() {
+ for (var methodNode in methodNodes) {
+ inferMethodIfNeeded(methodNode);
+ }
for (var accessorNode in accessorNodes) {
if (fusedTopLevelInference) {
assert(expandedTopLevelInference);
@@ -233,10 +266,22 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
/// a previous call to [createTopLevelTypeInferrer].
TypeInferrerImpl getMemberTypeInferrer(KernelMember member);
+ DartType getNamedParameterType(FunctionType functionType, String name) {
+ return functionType.getNamedParameter(name) ?? const DynamicType();
+ }
+
+ DartType getPositionalParameterType(FunctionType functionType, int i) {
+ if (i < functionType.positionalParameters.length) {
+ return functionType.positionalParameters[i];
+ } else {
+ return const DynamicType();
Siggi Cherem (dart-lang) 2017/06/21 20:19:08 report an error instead? (same thing when the name
Paul Berry 2017/06/21 20:54:47 Good question--I should have been clearer here. L
+ }
+ }
+
/// Performs type inference on the given [accessorNode].
void inferAccessor(AccessorNode accessorNode) {
- assert(accessorNode.state == AccessorState.NotInferredYet);
- accessorNode.state = AccessorState.Inferring;
+ assert(accessorNode.state == InferenceState.NotInferredYet);
+ accessorNode.state = InferenceState.Inferring;
var member = accessorNode.member;
if (strongMode) {
var inferredType = tryInferAccessorByInheritance(accessorNode);
@@ -255,14 +300,14 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
inferredType = const DynamicType();
}
}
- if (accessorNode.state == AccessorState.Inferred) {
+ if (accessorNode.state == InferenceState.Inferred) {
// A circularity must have been detected; at the time it was detected,
// inference for this node was completed.
return;
}
member.setInferredType(this, typeInferrer.uri, inferredType);
}
- accessorNode.state = AccessorState.Inferred;
+ accessorNode.state = InferenceState.Inferred;
// 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
@@ -279,7 +324,7 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
var member = accessorNode.member;
// TODO(paulberry): report the appropriate error.
var uri = getMemberTypeInferrer(member).uri;
- accessorNode.state = AccessorState.Inferred;
+ accessorNode.state = InferenceState.Inferred;
member.setInferredType(this, uri, const DynamicType());
// TODO(paulberry): the following is a hack so that outlines don't contain
// initializers. But it means that we rebuild the initializers when doing
@@ -292,10 +337,10 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
/// Performs fused type inference on the given [accessorNode].
void inferAccessorFused(AccessorNode accessorNode, AccessorNode dependant) {
switch (accessorNode.state) {
- case AccessorState.Inferred:
+ case InferenceState.Inferred:
// Already inferred. Nothing to do.
break;
- case AccessorState.Inferring:
+ case InferenceState.Inferring:
// An accessor depends on itself (possibly by way of intermediate
// accessors). Mark all accessors involved as circular and infer a type
// of `dynamic` for them.
@@ -307,7 +352,7 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
node = nextNode;
}
break;
- case AccessorState.NotInferredYet:
+ case InferenceState.NotInferredYet:
// Mark the "dependant" accessor (if any) as depending on this one, and
// invoke accessor inference for this node.
dependant?.currentDependency = accessorNode;
@@ -319,6 +364,26 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
}
}
+ /// Performs type inference on the given [methodNode].
+ void inferMethodIfNeeded(MethodNode methodNode) {
+ switch (methodNode.state) {
+ case InferenceState.Inferred:
+ // Already inferred. Nothing to do.
+ break;
+ case InferenceState.Inferring:
+ // An method depends on itself (possibly by way of intermediate
+ // methods). This should never happen, because it would require a
+ // circular class hierarchy (which Fasta prevents).
+ internalError('Circular method inference');
+ break;
+ case InferenceState.NotInferredYet:
+ methodNode.state = InferenceState.Inferring;
+ _inferMethod(methodNode);
+ methodNode.state = InferenceState.Inferred;
+ break;
+ }
+ }
+
@override
void prepareTopLevel(CoreTypes coreTypes, ClassHierarchy hierarchy) {
this.coreTypes = coreTypes;
@@ -334,7 +399,11 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
@override
void recordMember(KernelMember member) {
- accessorNodes.add(createAccessorNode(member));
+ if (member is KernelProcedure && !member.isGetter && !member.isSetter) {
+ methodNodes.add(createMethodNode(member));
+ } else {
+ accessorNodes.add(createAccessorNode(member));
+ }
}
DartType tryInferAccessorByInheritance(AccessorNode accessorNode) {
@@ -353,6 +422,47 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
return inferredType;
}
+ List<FunctionType> _computeMethodOverriddenTypes(MethodNode methodNode) {
+ var overriddenTypes = <FunctionType>[];
+ for (var override in methodNode.overrides) {
+ MethodNode overrideNode = KernelProcedure.getMethodNode(override);
+ if (overrideNode != null) {
+ inferMethodIfNeeded(overrideNode);
+ }
+ if (override.function == null) {
+ // This can happen if there are errors. Just skip this override.
+ continue;
+ }
+ var overriddenType = override.function.functionType;
+ var superclass = override.enclosingClass;
+ if (!superclass.typeParameters.isEmpty) {
+ var thisClass = methodNode.procedure.enclosingClass;
+ var superclassInstantiation = classHierarchy
+ .getClassAsInstanceOf(thisClass, superclass)
+ .asInterfaceType;
+ overriddenType = Substitution
+ .fromInterfaceType(superclassInstantiation)
+ .substituteType(overriddenType);
+ }
+ var methodTypeParameters = methodNode.procedure.function.typeParameters;
+ if (overriddenType.typeParameters.length != methodTypeParameters.length) {
+ // Generic arity mismatch. Don't do any inference for this method.
+ // TODO(paulberry): report an error.
+ return <FunctionType>[];
+ } else if (overriddenType.typeParameters.isNotEmpty) {
+ var substitutionMap = <TypeParameter, DartType>{};
+ for (int i = 0; i < methodTypeParameters.length; i++) {
+ substitutionMap[overriddenType.typeParameters[i]] =
+ new TypeParameterType(methodTypeParameters[i]);
+ }
+ overriddenType = substituteTypeParams(
+ overriddenType, substitutionMap, methodTypeParameters);
+ }
+ overriddenTypes.add(overriddenType);
+ }
+ return overriddenTypes;
+ }
+
DartType _computeOverriddenAccessorType(
Member override, AccessorNode accessorNode) {
if (fusedTopLevelInference) {
@@ -398,6 +508,71 @@ abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
// No matching field. The error should be reported elsewhere.
return const DynamicType();
}
+
+ void _inferMethod(MethodNode methodNode) {
+ var typeInferrer = getMemberTypeInferrer(methodNode.procedure);
+
+ // First collect types of overridden methods
+ var overriddenTypes = _computeMethodOverriddenTypes(methodNode);
+
+ // Now infer types.
+ DartType matchTypes(Iterable<DartType> types) {
+ if (!strongMode) return const DynamicType();
+ var iterator = types.iterator;
+ if (!iterator.moveNext()) {
+ // No overridden types. Infer `dynamic`.
+ return const DynamicType();
+ }
+ var inferredType = iterator.current;
+ while (iterator.moveNext()) {
+ if (inferredType != iterator.current) {
+ // TODO(paulberry): Types don't match. Report an error.
Siggi Cherem (dart-lang) 2017/06/21 20:19:08 Random questions: When not doing inference, are t
Paul Berry 2017/06/21 20:54:47 Peter has added some checks, but I believe that he
+ return const DynamicType();
+ }
+ }
+ return inferredType;
+ }
+
+ if (KernelProcedure.hasImplicitReturnType(methodNode.procedure)) {
+ var inferredType =
+ matchTypes(overriddenTypes.map((type) => type.returnType));
+ instrumentation?.record(
+ Uri.parse(typeInferrer.uri),
+ methodNode.procedure.fileOffset,
+ 'topType',
+ new InstrumentationValueForType(inferredType));
+ methodNode.procedure.function.returnType = inferredType;
+ }
+ var positionalParameters =
+ methodNode.procedure.function.positionalParameters;
+ for (int i = 0; i < positionalParameters.length; i++) {
+ if (KernelVariableDeclaration
+ .isImplicitlyTyped(positionalParameters[i])) {
+ var inferredType = matchTypes(
+ overriddenTypes.map((type) => getPositionalParameterType(type, i)));
+ instrumentation?.record(
+ Uri.parse(typeInferrer.uri),
+ positionalParameters[i].fileOffset,
+ 'topType',
+ new InstrumentationValueForType(inferredType));
+ positionalParameters[i].type = inferredType;
+ }
+ }
+ var namedParameters = methodNode.procedure.function.namedParameters;
+ for (int i = 0; i < namedParameters.length; i++) {
+ if (KernelVariableDeclaration.isImplicitlyTyped(namedParameters[i])) {
+ var name = namedParameters[i].name;
+ var inferredType = matchTypes(
+ overriddenTypes.map((type) => getNamedParameterType(type, name)));
+ instrumentation?.record(
+ Uri.parse(typeInferrer.uri),
+ namedParameters[i].fileOffset,
+ 'topType',
+ new InstrumentationValueForType(inferredType));
+ namedParameters[i].type = inferredType;
+ }
+ }
+ }
}
/// Subtype of [dependencyWalker.DependencyWalker] which is specialized to

Powered by Google App Engine
This is Rietveld 408576698