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

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

Issue 2856383003: Begin implementing subtype matching for type inference. (Closed)
Patch Set: Change test library URI 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
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
new file mode 100644
index 0000000000000000000000000000000000000000..2030fccfb5f0db4d6f70c4ed1457aaca3cfd6bfa
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
@@ -0,0 +1,238 @@
+// 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/fasta/type_inference/type_schema.dart';
+import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
+import 'package:kernel/ast.dart';
+
+/// Attempts to find a set of constraints for the given [typeParameters] under
+/// which [subtype] is a subtype of [supertype]. If such a set can be found, it
Jennifer Messerly 2017/05/04 18:06:01 very very minor style thing, first sentence of a d
Paul Berry 2017/05/04 19:56:05 Done.
+/// is returned (as a map from type parameter to constraint). If it can't,
+/// `null` is returned.
+Map<TypeParameter, TypeConstraint> subtypeMatch(
+ TypeSchemaEnvironment environment,
+ Iterable<TypeParameter> typeParameters,
+ DartType subtype,
+ DartType supertype) {
+ var typeConstraintGatherer =
+ new _TypeConstraintGatherer(environment, typeParameters);
+ if (typeConstraintGatherer.isSubtypeMatch(subtype, supertype)) {
+ return typeConstraintGatherer.computeConstraints();
+ } else {
+ return null;
+ }
+}
+
+class _ProtoConstraint {
Jennifer Messerly 2017/05/04 18:06:01 maybe add a doc comment on what this is tracking?
Paul Berry 2017/05/04 19:56:05 Done.
+ final TypeParameter parameter;
+
+ final DartType bound;
+
+ final bool isUpper;
+
+ _ProtoConstraint.lower(this.parameter, this.bound) : isUpper = false;
+
+ _ProtoConstraint.upper(this.parameter, this.bound) : isUpper = true;
+}
+
+/// Creates a collection of [TypeConstraint]s corresponding to type parameters,
+/// based on an attempt to make one type schema a subtype of another.
+class _TypeConstraintGatherer {
+ final TypeSchemaEnvironment environment;
+
+ final _protoConstraints = <_ProtoConstraint>[];
+
+ final List<TypeParameter> _parametersToConstrain;
+
+ /// Creates a [TypeConstraintGatherer] which is prepared to gather type
+ /// constraints for the given [typeParameters].
+ _TypeConstraintGatherer(
+ this.environment, Iterable<TypeParameter> typeParameters)
+ : _parametersToConstrain = typeParameters.toList();
+
+ /// Returns the set of type constraints that was gathered.
+ Map<TypeParameter, TypeConstraint> computeConstraints() {
+ var result = <TypeParameter, TypeConstraint>{};
+ for (var parameter in _parametersToConstrain) {
+ result[parameter] = new TypeConstraint();
+ }
+ for (var protoConstraint in _protoConstraints) {
+ if (protoConstraint.isUpper) {
+ environment.addUpperBound(
+ result[protoConstraint.parameter], protoConstraint.bound);
+ } else {
+ environment.addLowerBound(
+ result[protoConstraint.parameter], protoConstraint.bound);
+ }
+ }
+ return result;
+ }
+
+ /// Attempts to match [subtype] as a subtype of [supertype], gathering any
+ /// constraints discovered in the process. If a set of constraints was found,
+ /// `true` is returned and the caller may proceed to call
+ /// [computeConstraints]. Otherwise, `false` is returned and the set of
+ /// gathered constraints is undefined.
+ bool isSubtypeMatch(DartType subtype, DartType supertype) {
Jennifer Messerly 2017/05/04 18:06:01 great comments and structure in this method, BTW!
Paul Berry 2017/05/04 19:56:07 Thanks, though Leaf deserves most of the credit, s
+ // The unknown type `?` is a subtype match for any type `Q` with no
+ // constraints.
+ if (subtype is UnknownType) return true;
+ // Any type `P` is a subtype match for the unknown type `?` with no
+ // constraints.
+ if (supertype is UnknownType) return true;
+ // A type variable `T` in `L` is a subtype match for any type schema `Q`:
+ // - Under constraint `T <: Q`.
+ if (subtype is TypeParameterType &&
+ _parametersToConstrain.contains(subtype.parameter)) {
+ _constrainUpper(subtype.parameter, supertype);
+ return true;
+ }
+ // A type schema `Q` is a subtype match for a type variable `T` in `L`:
+ // - Under constraint `Q <: T`.
+ if (supertype is TypeParameterType &&
+ _parametersToConstrain.contains(supertype.parameter)) {
+ _constrainLower(supertype.parameter, subtype);
+ return true;
+ }
+ // Any two equal types `P` and `Q` are subtype matches under no constraints.
+ // Note: to avoid making the algorithm quadratic, we just check for
+ // identical(). If P and Q are equal but not identical, recursing through
+ // the types will give the proper result.
+ if (identical(subtype, supertype)) return true;
+ // Any type `P` is a subtype match for `dynamic`, `Object`, or `void` under
+ // no constraints.
+ if (_isTop(supertype)) return true;
+ // `Null` is a subtype match for any type `Q` under no constraints.
Jennifer Messerly 2017/05/04 18:06:01 it may be worth a note that non-nullable types wil
Paul Berry 2017/05/04 19:56:05 Done.
+ if (_isNull(subtype)) return true;
+ // `FutureOr<P>` is a subtype match for `FutureOr<Q>` with respect to `L`
+ // under constraints `C`:
+ // - If `P` is a subtype match for `Q` with respect to `L` under constraints
+ // `C`.
+ // TODO(paulberry): implement this case.
+ // `FutureOr<P>` is a subtype match for `Q` with respect to `L` under
+ // constraints `C0 + C1`:
+ // - If `Future<P>` is a subtype match for `Q` with respect to `L` under
+ // constraints `C0`.
+ // - And `P` is a subtype match for `Q` with respect to `L` under
+ // constraints `C1`.
+ // TODO(paulberry): implement this case.
+ // `P` is a subtype match for `FutureOr<Q>` with respect to `L` under
+ // constraints `C`:
+ // - If `P` is a subtype match for `Future<Q>` with respect to `L` under
+ // constraints `C`.
+ // - Or `P` is not a subtype match for `Future<Q>` with respect to `L` under
+ // constraints `C`
+ // - And `P` is a subtype match for `Q` with respect to `L` under
+ // constraints `C`
+ // TODO(paulberry): implement this case.
+ // A type variable `T` not in `L` with bound `P` is a subtype match for the
+ // same type variable `T` with bound `Q` with respect to `L` under
+ // constraints `C`:
+ // - If `P` is a subtype match for `Q` with respect to `L` under constraints
+ // `C`.
+ if (subtype is TypeParameterType) {
+ if (supertype is TypeParameterType &&
+ identical(subtype.parameter, supertype.parameter)) {
+ // Kernel doesn't yet allow a type variable to have different bounds
+ // under different circumstances (see dartbug.com/29529) so for now if
+ // we get here, the bounds must be the same.
+ // TODO(paulberry): update this code once dartbug.com/29529 is
+ // addressed.
+ return true;
+ }
+ // A type variable `T` not in `L` with bound `P` is a subtype match for a
+ // type `Q` with respect to `L` under constraints `C`:
+ // - If `P` is a subtype match for `Q` with respect to `L` under
+ // constraints `C`.
+ return isSubtypeMatch(subtype.parameter.bound, supertype);
+ }
+ if (subtype is InterfaceType && supertype is InterfaceType) {
+ return _isInterfaceSubtypeMatch(subtype, supertype);
+ }
+ // A type `P` is a subtype match for `Function` with respect to `L` under no
+ // constraints:
+ // - If `P` implements a call method.
+ // - Or if `P` is a function type.
+ // TODO(paulberry): implement this case.
+ // A type `P` is a subtype match for a type `Q` with respect to `L` under
+ // constraints `C`:
+ // - If `P` is an interface type which implements a call method of type `F`,
+ // 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);
Jennifer Messerly 2017/05/04 18:06:01 I think this is right, but just checking my unders
Paul Berry 2017/05/04 19:56:05 Yes, that's my understanding as well.
+ 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) =>
Jennifer Messerly 2017/05/04 18:06:01 how would you feel about calling this "_isBottom"
Paul Berry 2017/05/04 19:56:05 Hmm, good point. At the moment I don't have a ver
+ type is InterfaceType &&
+ identical(type.classNode, environment.coreTypes.nullClass);
+
+ bool _isTop(DartType type) =>
+ type is DynamicType ||
+ type is VoidType ||
+ (type is InterfaceType &&
+ identical(type.classNode, environment.coreTypes.objectClass));
+}

Powered by Google App Engine
This is Rietveld 408576698