Skip to content

Commit 517fb45

Browse files
committed
RequirementMachine: Fix minimal conformances algorithm to cope with unordered concrete conformances
1 parent b497c79 commit 517fb45

File tree

2 files changed

+58
-10
lines changed

2 files changed

+58
-10
lines changed

lib/AST/RequirementMachine/MinimalConformances.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -418,16 +418,25 @@ void MinimalConformances::collectConformanceRules() {
418418

419419
// Sort the list of conformance rules in reverse order; we're going to try
420420
// to minimize away less canonical rules first.
421-
std::sort(ConformanceRules.begin(), ConformanceRules.end(),
422-
[&](unsigned lhs, unsigned rhs) -> bool {
423-
const auto &lhsRule = System.getRule(lhs);
424-
const auto &rhsRule = System.getRule(rhs);
425-
426-
if (lhsRule.isExplicit() != rhsRule.isExplicit())
427-
return !lhsRule.isExplicit();
428-
429-
return *lhsRule.getLHS().compare(rhsRule.getLHS(), Context) > 0;
430-
});
421+
std::stable_sort(ConformanceRules.begin(), ConformanceRules.end(),
422+
[&](unsigned lhs, unsigned rhs) -> bool {
423+
const auto &lhsRule = System.getRule(lhs);
424+
const auto &rhsRule = System.getRule(rhs);
425+
426+
if (lhsRule.isExplicit() != rhsRule.isExplicit())
427+
return !lhsRule.isExplicit();
428+
429+
auto result = lhsRule.getLHS().compare(rhsRule.getLHS(), Context);
430+
431+
// Concrete conformance rules are unordered if they name the
432+
// same protocol but have different types. This can come up
433+
// if we have a class inheritance relationship 'Derived : Base',
434+
// and Base conforms to a protocol:
435+
//
436+
// T.[Base : P] => T
437+
// T.[Derived : P] => T
438+
return (result ? *result > 0 : 0);
439+
});
431440

432441
Context.ConformanceRulesHistogram.add(ConformanceRules.size());
433442
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s
2+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-inferred-signatures=on -disable-requirement-machine-concrete-contraction 2>&1 | %FileCheck %s
3+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-inferred-signatures=on -disable-requirement-machine-concrete-contraction -dump-requirement-machine 2>&1 | %FileCheck %s --check-prefix=RULE
4+
5+
protocol P {}
6+
7+
class Base : P {}
8+
9+
class Derived : Base {}
10+
11+
struct G<X> {}
12+
13+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G
14+
// CHECK-NEXT: Generic signature: <X where X == Derived>
15+
extension G where X : Base, X : P, X == Derived {}
16+
17+
// RULE: + superclass: τ_0_0 Base
18+
// RULE: + conforms_to: τ_0_0 P
19+
// RULE: + same_type: τ_0_0 Derived
20+
21+
// RULE: Rewrite system: {
22+
// RULE-NEXT: - [P].[P] => [P] [permanent]
23+
// RULE-NEXT: - τ_0_0.[superclass: Base] => τ_0_0 [explicit]
24+
// RULE-NEXT: - τ_0_0.[P] => τ_0_0 [explicit]
25+
// RULE-NEXT: - τ_0_0.[concrete: Derived] => τ_0_0 [explicit]
26+
// RULE-NEXT: - τ_0_0.[layout: _NativeClass] => τ_0_0
27+
// RULE-NEXT: - τ_0_0.[superclass: Derived] => τ_0_0
28+
// RULE-NEXT: - τ_0_0.[concrete: Derived : P] => τ_0_0
29+
// RULE-NEXT: - τ_0_0.[concrete: Base : P] => τ_0_0
30+
// RULE-NEXT: }
31+
32+
// Notes:
33+
//
34+
// - concrete contraction kills the 'X : P' requirement before building the
35+
// rewrite system so this test passes trivially.
36+
//
37+
// - without concrete contraction, we used to hit a problem where the sort
38+
// in the minimal conformances algorithm would hit the unordered concrete
39+
// conformances [concrete: Derived : P] vs [concrete: Base : P].

0 commit comments

Comments
 (0)