Skip to content

Commit 4f2aa5f

Browse files
committed
RequirementMachine: Implement superclass unification
Just as with concrete types, if we find that the same suffix has two different superclass symbols in the property map, we need to introduce same-type requirements between their generic parameters. The added wrinkle is that the classes might be different; in this case, one must be a superclass of the other, and we repeatedly substitute the generic arguments until we get the generic arguments of the superclass before we unify.
1 parent f0bec29 commit 4f2aa5f

File tree

5 files changed

+282
-19
lines changed

5 files changed

+282
-19
lines changed

lib/AST/RequirementMachine/PropertyMap.cpp

Lines changed: 109 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,9 @@ remapConcreteSubstitutionSchema(CanType concreteType,
335335
}
336336

337337
namespace {
338+
/// Utility class used by unifyConcreteTypes() and unifySuperclasses()
339+
/// to walk two concrete types in parallel. Any time there is a mismatch,
340+
/// records a new induced rule.
338341
class ConcreteTypeMatcher : public TypeMatcher<ConcreteTypeMatcher> {
339342
ArrayRef<Term> lhsSubstitutions;
340343
ArrayRef<Term> rhsSubstitutions;
@@ -357,14 +360,16 @@ namespace {
357360

358361
bool mismatch(TypeBase *firstType, TypeBase *secondType,
359362
Type sugaredFirstType) {
360-
if (isa<GenericTypeParamType>(firstType) &&
361-
isa<GenericTypeParamType>(secondType)) {
363+
bool firstAbstract = firstType->isTypeParameter();
364+
bool secondAbstract = secondType->isTypeParameter();
365+
366+
if (firstAbstract && secondAbstract) {
362367
// Both sides are type parameters; add a same-type requirement.
363-
unsigned lhsIndex = getGenericParamIndex(firstType);
364-
unsigned rhsIndex = getGenericParamIndex(secondType);
365-
if (lhsSubstitutions[lhsIndex] != rhsSubstitutions[rhsIndex]) {
366-
MutableTerm lhsTerm(lhsSubstitutions[lhsIndex]);
367-
MutableTerm rhsTerm(rhsSubstitutions[rhsIndex]);
368+
auto lhsTerm = getRelativeTermForType(CanType(firstType),
369+
lhsSubstitutions, ctx);
370+
auto rhsTerm = getRelativeTermForType(CanType(secondType),
371+
rhsSubstitutions, ctx);
372+
if (lhsTerm != rhsTerm) {
368373
if (debug) {
369374
llvm::dbgs() << "%% Induced rule " << lhsTerm
370375
<< " == " << rhsTerm << "\n";
@@ -374,12 +379,11 @@ namespace {
374379
return true;
375380
}
376381

377-
if (isa<GenericTypeParamType>(firstType) &&
378-
!isa<GenericTypeParamType>(secondType)) {
382+
if (firstAbstract && !secondAbstract) {
379383
// A type parameter is equated with a concrete type; add a concrete
380384
// type requirement.
381-
unsigned lhsIndex = getGenericParamIndex(firstType);
382-
MutableTerm subjectTerm(lhsSubstitutions[lhsIndex]);
385+
auto subjectTerm = getRelativeTermForType(CanType(firstType),
386+
lhsSubstitutions, ctx);
383387

384388
SmallVector<Term, 3> result;
385389
auto concreteType = remapConcreteSubstitutionSchema(CanType(secondType),
@@ -397,12 +401,11 @@ namespace {
397401
return true;
398402
}
399403

400-
if (!isa<GenericTypeParamType>(firstType) &&
401-
isa<GenericTypeParamType>(secondType)) {
404+
if (!firstAbstract && secondAbstract) {
402405
// A concrete type is equated with a type parameter; add a concrete
403406
// type requirement.
404-
unsigned rhsIndex = getGenericParamIndex(secondType);
405-
MutableTerm subjectTerm(rhsSubstitutions[rhsIndex]);
407+
auto subjectTerm = getRelativeTermForType(CanType(secondType),
408+
rhsSubstitutions, ctx);
406409

407410
SmallVector<Term, 3> result;
408411
auto concreteType = remapConcreteSubstitutionSchema(CanType(firstType),
@@ -420,7 +423,7 @@ namespace {
420423
return true;
421424
}
422425

423-
// Any other kind of type mismatch involves different concrete types on
426+
// Any other kind of type mismatch involves conflicting concrete types on
424427
// both sides, which can only happen on invalid input.
425428
return false;
426429
}
@@ -477,6 +480,87 @@ static bool unifyConcreteTypes(
477480
return false;
478481
}
479482

483+
/// When a type parameter has two superclasses, we have to both unify the
484+
/// type constructor arguments, and record the most derived superclass.
485+
///
486+
///
487+
/// For example, if we have this setup:
488+
///
489+
/// class Base<T, T> {}
490+
/// class Middle<U> : Base<T, T> {}
491+
/// class Derived : Middle<Int> {}
492+
///
493+
/// T : Base<U, V>
494+
/// T : Derived
495+
///
496+
/// The most derived superclass requirement is 'T : Derived'.
497+
///
498+
/// The corresponding superclass of 'Derived' is 'Base<Int, Int>', so we
499+
/// unify the type constructor arguments of 'Base<U, V>' and 'Base<Int, Int>',
500+
/// which generates two induced rules:
501+
///
502+
/// U.[concrete: Int] => U
503+
/// V.[concrete: Int] => V
504+
///
505+
/// Returns the most derived superclass, which becomes the new superclass
506+
/// that gets recorded in the property map.
507+
static Symbol unifySuperclasses(
508+
Symbol lhs, Symbol rhs, RewriteContext &ctx,
509+
SmallVectorImpl<std::pair<MutableTerm, MutableTerm>> &inducedRules,
510+
bool debug) {
511+
if (debug) {
512+
llvm::dbgs() << "% Unifying " << lhs << " with " << rhs << "\n";
513+
}
514+
515+
auto lhsType = lhs.getSuperclass();
516+
auto rhsType = rhs.getSuperclass();
517+
518+
auto *lhsClass = lhsType.getClassOrBoundGenericClass();
519+
assert(lhsClass != nullptr);
520+
521+
auto *rhsClass = rhsType.getClassOrBoundGenericClass();
522+
assert(rhsClass != nullptr);
523+
524+
// First, establish the invariant that lhsClass is either equal to, or
525+
// is a superclass of rhsClass.
526+
if (lhsClass == rhsClass ||
527+
lhsClass->isSuperclassOf(rhsClass)) {
528+
// Keep going.
529+
} else if (rhsClass->isSuperclassOf(lhsClass)) {
530+
std::swap(rhs, lhs);
531+
std::swap(rhsType, lhsType);
532+
std::swap(rhsClass, lhsClass);
533+
} else {
534+
// FIXME: Diagnose the conflict.
535+
if (debug) {
536+
llvm::dbgs() << "%% Unrelated superclass types\n";
537+
}
538+
539+
return lhs;
540+
}
541+
542+
if (lhsClass != rhsClass) {
543+
// Get the corresponding substitutions for the right hand side.
544+
assert(lhsClass->isSuperclassOf(rhsClass));
545+
rhsType = rhsType->getSuperclassForDecl(lhsClass)
546+
->getCanonicalType();
547+
}
548+
549+
// Unify type contructor arguments.
550+
ConcreteTypeMatcher matcher(lhs.getSubstitutions(),
551+
rhs.getSubstitutions(),
552+
ctx, inducedRules, debug);
553+
if (!matcher.match(lhsType, rhsType)) {
554+
if (debug) {
555+
llvm::dbgs() << "%% Superclass conflict\n";
556+
}
557+
return rhs;
558+
}
559+
560+
// Record the more specific class.
561+
return rhs;
562+
}
563+
480564
void PropertyBag::addProperty(
481565
Symbol property, RewriteContext &ctx,
482566
SmallVectorImpl<std::pair<MutableTerm, MutableTerm>> &inducedRules,
@@ -496,9 +580,15 @@ void PropertyBag::addProperty(
496580
return;
497581

498582
case Symbol::Kind::Superclass: {
499-
// FIXME: This needs to find the most derived subclass and also call
500-
// unifyConcreteTypes()
501-
Superclass = property;
583+
// FIXME: Also handle superclass vs concrete
584+
585+
if (Superclass) {
586+
Superclass = unifySuperclasses(*Superclass, property,
587+
ctx, inducedRules, debug);
588+
} else {
589+
Superclass = property;
590+
}
591+
502592
return;
503593
}
504594

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %target-typecheck-verify-swift -requirement-machine=verify -debug-requirement-machine 2>&1 | %FileCheck %s
2+
3+
class Base {}
4+
class Derived : Base {
5+
func derivedMethod() {}
6+
}
7+
8+
protocol P : Base {}
9+
10+
func takesDerived(_: Derived) {}
11+
12+
extension P where Self : Derived {
13+
func passesDerived() { derivedMethod() }
14+
}
15+
16+
// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : Derived, τ_0_0 : P>
17+
// CHECK-NEXT: Rewrite system: {
18+
// CHECK-NEXT: - [P].[superclass: Base] => [P]
19+
// CHECK-NEXT: - [P].[layout: _NativeClass] => [P]
20+
// CHECK-NEXT: - τ_0_0.[superclass: Derived] => τ_0_0
21+
// CHECK-NEXT: - τ_0_0.[layout: _NativeClass] => τ_0_0
22+
// CHECK-NEXT: - τ_0_0.[P] => τ_0_0
23+
// CHECK-NEXT: - τ_0_0.[superclass: Base] => τ_0_0
24+
// CHECK-NEXT: }
25+
// CHECK-NEXT: Property map: {
26+
// CHECK-NEXT: [P] => { layout: _NativeClass superclass: [superclass: Base] }
27+
// CHECK-NEXT: τ_0_0 => { conforms_to: [P] layout: _NativeClass superclass: [superclass: Derived] }
28+
// CHECK-NEXT: }
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %target-typecheck-verify-swift -requirement-machine=on -debug-requirement-machine 2>&1 | %FileCheck %s
2+
3+
// Note: The GSB fails this test, because it doesn't implement unification of
4+
// superclass type constructor arguments.
5+
6+
class Generic<T, U, V> {}
7+
8+
protocol P1 {
9+
associatedtype X : Generic<Int, A1, B1>
10+
associatedtype A1
11+
associatedtype B1
12+
}
13+
14+
protocol P2 {
15+
associatedtype X : Generic<A2, String, B2>
16+
associatedtype A2
17+
associatedtype B2
18+
}
19+
20+
func sameType<T>(_: T.Type, _: T.Type) {}
21+
22+
func takesGenericIntString<U>(_: Generic<Int, String, U>.Type) {}
23+
24+
func unifySuperclassTest<T : P1 & P2>(_: T) {
25+
sameType(T.A1.self, String.self)
26+
sameType(T.A2.self, Int.self)
27+
sameType(T.B1.self, T.B2.self)
28+
takesGenericIntString(T.X.self)
29+
}
30+
31+
// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2>
32+
// CHECK-NEXT: Rewrite system: {
33+
// CHECK: - τ_0_0.[P1&P2:X].[superclass: Generic<τ_0_0, String, τ_0_1> with <τ_0_0.[P2:A2], τ_0_0.[P2:B2]>] => τ_0_0.[P1&P2:X]
34+
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[superclass: Generic<Int, τ_0_0, τ_0_1> with <τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] => τ_0_0.[P1&P2:X]
35+
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[layout: _NativeClass] => τ_0_0.[P1&P2:X]
36+
// CHECK-NEXT: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2]
37+
// CHECK-NEXT: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1]
38+
// CHECK-NEXT: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1]
39+
// CHECK: }
40+
// CHECK-NEXT: Property map: {
41+
// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Generic<Int, τ_0_0, τ_0_1> with <[P1:A1], [P1:B1]>] }
42+
// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<τ_0_0, String, τ_0_1> with <[P2:A2], [P2:B2]>] }
43+
// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] }
44+
// CHECK-NEXT: τ_0_0.[P1&P2:X] => { layout: _NativeClass superclass: [superclass: Generic<Int, τ_0_0, τ_0_1> with <τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] }
45+
// CHECK-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] }
46+
// CHECK-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] }
47+
// CHECK-NEXT: }
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %target-typecheck-verify-swift -requirement-machine=on -debug-requirement-machine 2>&1 | %FileCheck %s
2+
3+
// Note: The GSB fails this test, because it doesn't implement unification of
4+
// superclass type constructor arguments.
5+
6+
class Generic<T, U, V> {}
7+
8+
class Derived<TT, UU> : Generic<Int, TT, UU> {}
9+
10+
protocol P1 {
11+
associatedtype X : Derived<A1, B1>
12+
associatedtype A1
13+
associatedtype B1
14+
}
15+
16+
protocol P2 {
17+
associatedtype X : Generic<A2, String, B2>
18+
associatedtype A2
19+
associatedtype B2
20+
}
21+
22+
func sameType<T>(_: T.Type, _: T.Type) {}
23+
24+
func takesDerivedString<U>(_: Derived<String, U>.Type) {}
25+
26+
func unifySuperclassTest<T : P1 & P2>(_: T) {
27+
sameType(T.A1.self, String.self)
28+
sameType(T.A2.self, Int.self)
29+
sameType(T.B1.self, T.B2.self)
30+
takesDerivedString(T.X.self)
31+
}
32+
33+
// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2>
34+
// CHECK-NEXT: Rewrite system: {
35+
// CHECK: - τ_0_0.[P1&P2:X].[superclass: Generic<τ_0_0, String, τ_0_1> with <τ_0_0.[P2:A2], τ_0_0.[P2:B2]>] => τ_0_0.[P1&P2:X]
36+
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[superclass: Derived<τ_0_0, τ_0_1> with <τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] => τ_0_0.[P1&P2:X]
37+
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[layout: _NativeClass] => τ_0_0.[P1&P2:X]
38+
// CHECK-NEXT: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2]
39+
// CHECK-NEXT: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1]
40+
// CHECK-NEXT: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1]
41+
// CHECK-NEXT: }
42+
// CHECK-NEXT: Property map: {
43+
// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0, τ_0_1> with <[P1:A1], [P1:B1]>] }
44+
// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<τ_0_0, String, τ_0_1> with <[P2:A2], [P2:B2]>] }
45+
// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] }
46+
// CHECK-NEXT: τ_0_0.[P1&P2:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0, τ_0_1> with <τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] }
47+
// CHECK-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] }
48+
// CHECK-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] }
49+
// CHECK-NEXT: }
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %target-typecheck-verify-swift -requirement-machine=on -debug-requirement-machine 2>&1 | %FileCheck %s
2+
3+
// Note: The GSB fails this test, because it doesn't implement unification of
4+
// superclass type constructor arguments.
5+
6+
class Base<T> {}
7+
8+
protocol Q {
9+
associatedtype T
10+
}
11+
12+
class Derived<TT : Q> : Base<TT.T> {}
13+
14+
protocol P1 {
15+
associatedtype X : Base<A1>
16+
associatedtype A1
17+
}
18+
19+
protocol P2 {
20+
associatedtype X : Derived<A2>
21+
associatedtype A2 : Q
22+
}
23+
24+
func sameType<T>(_: T.Type, _: T.Type) {}
25+
26+
func takesBase<U>(_: Base<U>.Type, _: U.Type) {}
27+
28+
func takesDerived<U : Q>(_: Derived<U>.Type, _: U.Type) {}
29+
30+
func unifySuperclassTest<T : P1 & P2>(_: T) {
31+
sameType(T.A1.self, T.A2.T.self)
32+
takesBase(T.X.self, T.A1.self)
33+
takesDerived(T.X.self, T.A2.self)
34+
}
35+
36+
// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2>
37+
// CHECK-NEXT: Rewrite system: {
38+
// CHECK: - τ_0_0.[P1&P2:X].[superclass: Derived<τ_0_0> with <τ_0_0.[P2:A2]>] => τ_0_0.[P1&P2:X]
39+
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[superclass: Base<τ_0_0> with <τ_0_0.[P1:A1]>] => τ_0_0.[P1&P2:X]
40+
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[layout: _NativeClass] => τ_0_0.[P1&P2:X]
41+
// CHECK-NEXT: - τ_0_0.[P2:A2].[Q:T] => τ_0_0.[P1:A1]
42+
// CHECK-NEXT: }
43+
// CHECK-NEXT: Property map: {
44+
// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Base<τ_0_0> with <[P1:A1]>] }
45+
// CHECK-NEXT: [P2:A2] => { conforms_to: [Q] }
46+
// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0> with <[P2:A2]>] }
47+
// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] }
48+
// CHECK-NEXT: τ_0_0.[P1&P2:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0> with <τ_0_0.[P2:A2]>] }
49+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)