Chromium Code Reviews| 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 |