Skip to content

Commit d39c667

Browse files
committed
RequirementMachine: New elimination order based on projection count
We want to prefer to eliminate rules that are not concrete type rules, unless non-concrete type rule in question is a projection. For example, suppose that this rule comes from a protocol: T.T == G<T.U, T.V> And these three rules are in our minimization domain: a) T.T == G<Int, W> b) T.U == Int c) T.V == W Then a) implies both b) and c), and vice versa. In this case, we want to keep T.U == Int and T.V == W, and eliminate T.T == G<Int, W>. T.T == G<Int, W> is concrete and T.V == W is not, but because T.V == W is defined by a projection, we still prefer to eliminate T.T == G<Int, W> over T.V == W.
1 parent aeceda3 commit d39c667

File tree

5 files changed

+172
-16
lines changed

5 files changed

+172
-16
lines changed

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -425,26 +425,42 @@ findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
425425
// we've found so far.
426426
const auto &otherRule = getRule(found->second);
427427

428-
unsigned ruleNesting = rule.getNesting();
429-
unsigned otherRuleNesting = otherRule.getNesting();
428+
const auto &loop = Loops[pair.first];
429+
const auto &otherLoop = Loops[found->first];
430430

431-
// If both rules are concrete type requirements, first compare nesting
432-
// depth. This breaks the tie when we have two rules that each imply
433-
// the other via an induced rule that comes from a protocol.
431+
// If one of the rules was a concrete unification projection, prefer to
432+
// eliminate the *other* rule.
434433
//
435-
// For example,
434+
// For example, if 'X.T == G<U, V>' is implied by the conformance on X,
435+
// and the following three rules are defined in the current protocol:
436436
//
437-
// T == G<Int>
438-
// U == Int
437+
// X.T == G<Int, W>
438+
// X.U == Int
439+
// X.V == W
439440
//
440-
// Where T == G<U> is implied elsewhere.
441-
if (ruleNesting > 0 && otherRuleNesting > 0) {
442-
if (ruleNesting > otherRuleNesting) {
441+
// Then we can either eliminate a) alone, or b) and c). Since b) and c)
442+
// are projections, they are "simpler", and we would rather keep both and
443+
// eliminate a).
444+
unsigned projectionCount = loop.getProjectionCount(*this);
445+
unsigned otherProjectionCount = otherLoop.getProjectionCount(*this);
446+
447+
if (projectionCount != otherProjectionCount) {
448+
if (projectionCount < otherProjectionCount)
443449
found = pair;
444-
continue;
445-
} else if (otherRuleNesting > ruleNesting) {
446-
continue;
447-
}
450+
451+
continue;
452+
}
453+
454+
// If one of the rules is a concrete type requirement, prefer to
455+
// eliminate the *other* rule.
456+
bool ruleIsConcrete = rule.getLHS().back().hasSubstitutions();
457+
bool otherRuleIsConcrete = otherRule.getRHS().back().hasSubstitutions();
458+
459+
if (ruleIsConcrete != otherRuleIsConcrete) {
460+
if (otherRuleIsConcrete)
461+
found = pair;
462+
463+
continue;
448464
}
449465

450466
// Otherwise, perform a shortlex comparison on (LHS, RHS).

test/Generics/concrete_conformance_minimization.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,20 @@ struct G2<T : SIMD> {}
1414

1515
// CHECK-LABEL: ExtensionDecl line={{[0-9]+}} base=G2
1616
// CHECK-NEXT: Generic signature: <T where T == SIMD2<Double>>
17-
extension G2 where T == SIMD2<Double> {}
17+
extension G2 where T == SIMD2<Double> {}
18+
19+
struct G3<T: SIMD> where T.Scalar: FixedWidthInteger & SignedInteger {}
20+
21+
// CHECK-LABEL: ExtensionDecl line={{[0-9]+}} base=G3
22+
// CHECK-NEXT: Generic signature: <T where T == SIMD2<Int8>>
23+
extension G3 where T == SIMD2<Int8> {}
24+
25+
struct G4<T : StringProtocol> {}
26+
27+
// CHECK-LABEL: ExtensionDecl line={{[0-9]+}} base=G4
28+
// CHECK-NEXT: Generic signature: <T where T == String>
29+
extension G4 where T == String {}
30+
31+
// CHECK-LABEL: ExtensionDecl line={{[0-9]+}} base=G4
32+
// CHECK-NEXT: Generic signature: <T where T == Substring>
33+
extension G4 where T == Substring {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s
2+
3+
protocol P1 {}
4+
5+
protocol P2 {
6+
associatedtype T: P1
7+
}
8+
9+
protocol P3: P1 {}
10+
11+
protocol P4: P3 {}
12+
13+
protocol P5: P2 {
14+
associatedtype U: P5 where U.T: P4
15+
}
16+
17+
protocol P6: P1 {
18+
associatedtype V: P6 & P4
19+
}
20+
21+
struct C: P6 & P4 {
22+
typealias V = C
23+
}
24+
25+
struct G1<T: P6>: P5 {
26+
typealias U = G1<T.V>
27+
}
28+
29+
struct G2<T: P5> where T.T: P4 {}
30+
31+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G2
32+
// CHECK-NEXT: Generic signature: <T where T == G1<C>>
33+
extension G2 where T == G1<C> {}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=verify 2>&1 | %FileCheck %s
2+
3+
protocol P1 {
4+
associatedtype T
5+
}
6+
7+
protocol P2 {}
8+
9+
protocol P3 {
10+
associatedtype T : P2, P3
11+
}
12+
13+
protocol P4 : P3 {}
14+
15+
protocol P5 : P4 {}
16+
17+
struct C : P2, P5 {
18+
typealias T = C
19+
}
20+
21+
struct G<T: P5> : P1 {}
22+
23+
// CHECK-LABEL: concrete_nesting_elimination_order.(file).P6@
24+
// CHECK-NEXT: Requirement signature: <Self where Self.[P6]X : P1, Self.[P6]Y : P5, Self.[P6]Y == Self.[P6]Z.[P1]T, Self.[P6]Z : P1>
25+
26+
protocol P6 {
27+
associatedtype X : P1
28+
associatedtype Y : P5
29+
associatedtype Z : P1 where Z.T == Y
30+
}
31+
32+
// CHECK-LABEL: concrete_nesting_elimination_order.(file).P7@
33+
// CHECK-NEXT: Requirement signature: <Self where Self : P6, Self.[P6]X == G<Self.[P6]Y>, Self.[P6]Z == G<Self.[P6]Y>>
34+
35+
protocol P7 : P6 where X == G<Y>, X == Z {}
36+
37+
// CHECK-LABEL: concrete_nesting_elimination_order.(file).P8a@
38+
// CHECK-NEXT: Requirement signature: <Self where Self : P7, Self.[P6]Y == C>
39+
40+
protocol P8a : P7 where Y == C {}
41+
42+
// CHECK-LABEL: concrete_nesting_elimination_order.(file).P8b@
43+
// CHECK-NEXT: Requirement signature: <Self where Self : P7, Self.[P6]Y == C>
44+
45+
protocol P8b : P7 where Y == C {}
46+
47+
// Make sure we pick 'Y == C' and not 'Y == G<C>' here.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
2+
3+
protocol P1 {
4+
associatedtype T
5+
}
6+
7+
protocol P2 {
8+
associatedtype X : P2 where X.X == X
9+
associatedtype Y : P1 where Y.T == A
10+
}
11+
12+
struct A : P2 {
13+
typealias X = A
14+
struct Y : P1 {
15+
typealias T = A
16+
}
17+
}
18+
19+
struct B : P2 {
20+
typealias X = A
21+
struct Y : P1 {
22+
typealias T = A
23+
}
24+
}
25+
26+
// CHECK-LABEL: concrete_nesting_elimination_order_2.(file).P3a@
27+
// CHECK-NEXT: Requirement signature: <Self where Self : P2, Self.[P2]X == A, Self.[P2]Y == B.Y>
28+
29+
protocol P3a : P2 where X == A, Y == B.Y {}
30+
31+
// CHECK-LABEL: concrete_nesting_elimination_order_2.(file).P3b@
32+
// CHECK-NEXT: Requirement signature: <Self where Self : P2, Self.[P2]X == A, Self.[P2]Y == B.Y>
33+
34+
protocol P3b : P2 where X == X.Y.T, Y == B.Y {}
35+
36+
// CHECK-LABEL: concrete_nesting_elimination_order_2.(file).P3ab@
37+
// CHECK-NEXT: Requirement signature: <Self where Self : P2, Self.[P2]X == A, Self.[P2]Y == B.Y>
38+
39+
protocol P3ab : P2 where X == A, X == X.Y.T, Y == B.Y {}
40+
41+
// CHECK-LABEL: concrete_nesting_elimination_order_2.(file).P3ba@
42+
// CHECK-NEXT: Requirement signature: <Self where Self : P2, Self.[P2]X == A, Self.[P2]Y == B.Y>
43+
44+
protocol P3ba : P2 where X == X.Y.T, X == A, Y == B.Y {}

0 commit comments

Comments
 (0)