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

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

Issue 2883133002: Add subtype matching rules for function types. (Closed)
Patch Set: Created 3 years, 7 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
« no previous file with comments | « no previous file | pkg/front_end/test/fasta/strong.status » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
index 2d01953f34c89a34608e978582a715323adeda3c..96fcea43a65c6834635f85b45b3783485d88c269 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
@@ -5,6 +5,7 @@
import 'package:front_end/src/fasta/type_inference/type_schema.dart';
import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
import 'package:kernel/ast.dart';
+import 'package:kernel/type_algebra.dart';
/// Creates a collection of [TypeConstraint]s corresponding to type parameters,
/// based on an attempt to make one type schema a subtype of another.
@@ -53,6 +54,143 @@ class TypeConstraintGatherer {
return isMatch;
}
+ void _constrainLower(TypeParameter parameter, DartType lower) {
+ _protoConstraints.add(new _ProtoConstraint.lower(parameter, lower));
+ }
+
+ void _constrainUpper(TypeParameter parameter, DartType upper) {
+ _protoConstraints.add(new _ProtoConstraint.upper(parameter, upper));
+ }
+
+ bool _isFunctionSubtypeMatch(FunctionType subtype, FunctionType supertype) {
+ // A function type `(M0,..., Mn, [M{n+1}, ..., Mm]) -> R0` is a subtype
+ // match for a function type `(N0,..., Nk, [N{k+1}, ..., Nr]) -> R1` with
+ // respect to `L` under constraints `C0 + ... + Cr + C`
+ // - If `R0` is a subtype match for a type `R1` with respect to `L` under
+ // constraints `C`:
+ // - If `n <= k` and `r <= m`.
+ // - And for `i` in `0...r`, `Ni` is a subtype match for `Mi` with respect
+ // to `L` under constraints `Ci`.
+ // Function types with named parameters are treated analogously to the
+ // positional parameter case above.
+ // A generic function type `<T0 extends B0, ..., Tn extends Bn>F0` is a
+ // subtype match for a generic function type `<S0 extends B0, ..., Sn
+ // extends Bn>F1` with respect to `L` under constraints `Cl`:
+ // - If `F0[Z0/T0, ..., Zn/Tn]` is a subtype match for `F0[Z0/S0, ...,
+ // Zn/Sn]` with respect to `L` under constraints `C`, where each `Zi` is a
+ // fresh type variable with bound `Bi`.
+ // - And `Cl` is `C` with each constraint replaced with its closure with
+ // respect to `[Z0, ..., Zn]`.
+ if (subtype.requiredParameterCount > supertype.requiredParameterCount) {
+ return false;
+ }
+ if (subtype.positionalParameters.length <
+ supertype.positionalParameters.length) {
+ return false;
+ }
+ if (subtype.typeParameters.isNotEmpty ||
+ supertype.typeParameters.isNotEmpty) {
+ var subtypeSubstitution = <TypeParameter, DartType>{};
+ var supertypeSubstitution = <TypeParameter, DartType>{};
+ if (!_matchTypeFormals(subtype.typeParameters, supertype.typeParameters,
+ subtypeSubstitution, supertypeSubstitution)) {
+ return false;
+ }
+
+ // TODO(paulberry): try to push this functionality into kernel.
+ FunctionType substituteTypeParams(
+ FunctionType type, Map<TypeParameter, DartType> substitutionMap) {
+ var substitution = Substitution.fromMap(substitutionMap);
+ return new FunctionType(
+ type.positionalParameters.map(substitution.substituteType).toList(),
+ substitution.substituteType(type.returnType),
+ namedParameters: type.namedParameters
+ .map((named) => new NamedType(
+ named.name, substitution.substituteType(named.type)))
+ .toList(),
+ typeParameters: substitutionMap.keys.toList(),
+ requiredParameterCount: type.requiredParameterCount);
+ }
+
+ subtype = substituteTypeParams(subtype, subtypeSubstitution);
+ supertype = substituteTypeParams(supertype, supertypeSubstitution);
+ }
+
+ // Test the return types.
+ if (supertype.returnType is! VoidType &&
+ !_isSubtypeMatch(subtype.returnType, supertype.returnType)) {
+ return false;
+ }
+
+ // Test the parameter types.
+ for (int i = 0; i < supertype.positionalParameters.length; ++i) {
+ var supertypeParameter = supertype.positionalParameters[i];
+ var subtypeParameter = subtype.positionalParameters[i];
+ // Termination: Both types shrink in size.
+ if (!_isSubtypeMatch(supertypeParameter, subtypeParameter)) {
+ return false;
+ }
+ }
+ int subtypeNameIndex = 0;
+ for (NamedType supertypeParameter in supertype.namedParameters) {
+ while (subtypeNameIndex < subtype.namedParameters.length &&
+ subtype.namedParameters[subtypeNameIndex].name !=
+ supertypeParameter.name) {
+ ++subtypeNameIndex;
+ }
+ if (subtypeNameIndex == subtype.namedParameters.length) return false;
+ NamedType subtypeParameter = subtype.namedParameters[subtypeNameIndex];
+ // Termination: Both types shrink in size.
+ if (!_isSubtypeMatch(supertypeParameter.type, subtypeParameter.type)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool _isInterfaceSubtypeMatch(
+ InterfaceType subtype, InterfaceType supertype) {
+ // A type `P<M0, ..., Mk>` is a subtype match for `P<N0, ..., Nk>` with
+ // respect to `L` under constraints `C0 + ... + Ck`:
+ // - If `Mi` is a subtype match for `Ni` with respect to `L` under
+ // constraints `Ci`.
+ // A type `P<M0, ..., Mk>` is a subtype match for `Q<N0, ..., Nj>` with
+ // respect to `L` under constraints `C`:
+ // - If `R<B0, ..., Bj>` is the superclass of `P<M0, ..., Mk>` and `R<B0,
+ // ..., Bj>` is a subtype match for `Q<N0, ..., Nj>` with respect to `L`
+ // under constraints `C`.
+ // - Or `R<B0, ..., Bj>` is one of the interfaces implemented by `P<M0, ...,
+ // Mk>` (considered in lexical order) and `R<B0, ..., Bj>` is a subtype
+ // match for `Q<N0, ..., Nj>` with respect to `L` under constraints `C`.
+ // - Or `R<B0, ..., Bj>` is a mixin into `P<M0, ..., Mk>` (considered in
+ // lexical order) and `R<B0, ..., Bj>` is a subtype match for `Q<N0, ...,
+ // Nj>` with respect to `L` under constraints `C`.
+
+ // Note that since kernel requires that no class may only appear in the set
+ // of supertypes of a given type more than once, the order of the checks
+ // above is irrelevant; we just need to find the matched superclass,
+ // substitute, and then iterate through type variables.
+ var matchingSupertypeOfSubtype =
+ environment.hierarchy.getTypeAsInstanceOf(subtype, supertype.classNode);
+ if (matchingSupertypeOfSubtype == null) return false;
+ for (int i = 0; i < supertype.classNode.typeParameters.length; i++) {
+ if (!_isSubtypeMatch(matchingSupertypeOfSubtype.typeArguments[i],
+ supertype.typeArguments[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool _isNull(DartType type) {
+ // TODO(paulberry): would it be better to call this "_isBottom", and to have
+ // it return `true` for both Null and bottom types? Revisit this once
+ // enough functionality is implemented that we can compare the behavior with
+ // the old analyzer-based implementation.
+ return type is InterfaceType &&
+ identical(type.classNode, environment.coreTypes.nullClass);
+ }
+
/// Attempts to match [subtype] as a subtype of [supertype], gathering any
/// constraints discovered in the process.
///
@@ -150,78 +288,21 @@ class TypeConstraintGatherer {
// and `F` is a subtype match for a type `Q` with respect to `L` under
// constraints `C`.
// TODO(paulberry): implement this case.
- // A function type `(M0,..., Mn, [M{n+1}, ..., Mm]) -> R0` is a subtype
- // match for a function type `(N0,..., Nk, [N{k+1}, ..., Nr]) -> R1` with
- // respect to `L` under constraints `C0 + ... + Cr + C`
- // - If `R0` is a subtype match for a type `R1` with respect to `L` under
- // constraints `C`:
- // - If `n <= k` and `r <= m`.
- // - And for `i` in `0...r`, `Ni` is a subtype match for `Mi` with respect
- // to `L` under constraints `Ci`.
- // Function types with named parameters are treated analogously to the
- // positional parameter case above.
- // TODO(paulberry): implement this case.
- // A generic function type `<T0 extends B0, ..., Tn extends Bn>F0` is a
- // subtype match for a generic function type `<S0 extends B0, ..., Sn
- // extends Bn>F1` with respect to `L` under constraints `Cl`:
- // - If `F0[Z0/T0, ..., Zn/Tn]` is a subtype match for `F0[Z0/S0, ...,
- // Zn/Sn]` with respect to `L` under constraints `C`, where each `Zi` is a
- // fresh type variable with bound `Bi`.
- // - And `Cl` is `C` with each constraint replaced with its closure with
- // respect to `[Z0, ..., Zn]`.
- // TODO(paulberry): implement this case.
- return false;
- }
-
- void _constrainLower(TypeParameter parameter, DartType lower) {
- _protoConstraints.add(new _ProtoConstraint.lower(parameter, lower));
- }
-
- void _constrainUpper(TypeParameter parameter, DartType upper) {
- _protoConstraints.add(new _ProtoConstraint.upper(parameter, upper));
- }
-
- bool _isInterfaceSubtypeMatch(
- InterfaceType subtype, InterfaceType supertype) {
- // A type `P<M0, ..., Mk>` is a subtype match for `P<N0, ..., Nk>` with
- // respect to `L` under constraints `C0 + ... + Ck`:
- // - If `Mi` is a subtype match for `Ni` with respect to `L` under
- // constraints `Ci`.
- // A type `P<M0, ..., Mk>` is a subtype match for `Q<N0, ..., Nj>` with
- // respect to `L` under constraints `C`:
- // - If `R<B0, ..., Bj>` is the superclass of `P<M0, ..., Mk>` and `R<B0,
- // ..., Bj>` is a subtype match for `Q<N0, ..., Nj>` with respect to `L`
- // under constraints `C`.
- // - Or `R<B0, ..., Bj>` is one of the interfaces implemented by `P<M0, ...,
- // Mk>` (considered in lexical order) and `R<B0, ..., Bj>` is a subtype
- // match for `Q<N0, ..., Nj>` with respect to `L` under constraints `C`.
- // - Or `R<B0, ..., Bj>` is a mixin into `P<M0, ..., Mk>` (considered in
- // lexical order) and `R<B0, ..., Bj>` is a subtype match for `Q<N0, ...,
- // Nj>` with respect to `L` under constraints `C`.
-
- // Note that since kernel requires that no class may only appear in the set
- // of supertypes of a given type more than once, the order of the checks
- // above is irrelevant; we just need to find the matched superclass,
- // substitute, and then iterate through type variables.
- var matchingSupertypeOfSubtype =
- environment.hierarchy.getTypeAsInstanceOf(subtype, supertype.classNode);
- if (matchingSupertypeOfSubtype == null) return false;
- for (int i = 0; i < supertype.classNode.typeParameters.length; i++) {
- if (!_isSubtypeMatch(matchingSupertypeOfSubtype.typeArguments[i],
- supertype.typeArguments[i])) {
- return false;
+ if (subtype is FunctionType) {
+ if (supertype is InterfaceType) {
+ if (identical(
+ supertype.classNode, environment.coreTypes.functionClass) ||
+ identical(supertype.classNode, environment.coreTypes.objectClass)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ if (supertype is FunctionType) {
+ return _isFunctionSubtypeMatch(subtype, supertype);
}
}
- return true;
- }
-
- bool _isNull(DartType type) {
- // TODO(paulberry): would it be better to call this "_isBottom", and to have
- // it return `true` for both Null and bottom types? Revisit this once
- // enough functionality is implemented that we can compare the behavior with
- // the old analyzer-based implementation.
- return type is InterfaceType &&
- identical(type.classNode, environment.coreTypes.nullClass);
+ return false;
}
bool _isTop(DartType type) =>
@@ -229,6 +310,37 @@ class TypeConstraintGatherer {
type is VoidType ||
(type is InterfaceType &&
identical(type.classNode, environment.coreTypes.objectClass));
+
+ /// Given two lists of function type formal parameters, checks that their
+ /// bounds are compatible.
+ ///
+ /// The return value indicates whether a match was found. If it was, entries
+ /// are added to [substitution1] and [substitution2] which substitute a fresh
+ /// set of type variables for the type parameters [params1] and [params2],
+ /// respectively, allowing further comparison.
+ bool _matchTypeFormals(
+ List<TypeParameter> params1,
+ List<TypeParameter> params2,
+ Map<TypeParameter, DartType> substitution1,
+ Map<TypeParameter, DartType> substitution2) {
+ int count = params1.length;
+ if (count != params2.length) return false;
+ // TODO(paulberry): in imitation of analyzer, we're checking the bounds as
+ // we build up the substitutions. But I don't think that's correct--I think
+ // we should build up both substitutions completely before checking any
+ // bounds.
+ for (int i = 0; i < count; i++) {
+ TypeParameter pFresh = new TypeParameter(params2[i].name);
+ DartType variableFresh = new TypeParameterType(pFresh);
+ substitution1[params1[i]] = variableFresh;
+ substitution2[params2[i]] = variableFresh;
+ DartType bound1 = substitute(params1[i].bound, substitution1);
+ DartType bound2 = substitute(params2[i].bound, substitution2);
+ pFresh.bound = bound2;
+ if (!_isSubtypeMatch(bound2, bound1)) return false;
+ }
+ return true;
+ }
}
/// Tracks a single constraint on a single type variable.
« no previous file with comments | « no previous file | pkg/front_end/test/fasta/strong.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698