Skip to content

Commit 39d486a

Browse files
committed
RequirementMachine: Try to avoid introducing rules of the form T.[P] => T.[Q]
These rules would be fine since RHS simplification eliminates them, but they cause problems for the minimal conformances algorithm. To avoid introducing such rules, ensure that the critical pair of two property-like rules is itself a property-like rule instead of relying on subsequent simplifications sorting it out. See the new comment in RewriteSystem::computeCriticalPair() for details. I need to understand this problem better and either fix minimal conformances or add stronger assertions, but for now this fixes the last failure with -requirement-machine-abstract-signatures=verify.
1 parent 0e4c398 commit 39d486a

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

lib/AST/RequirementMachine/KnuthBendix.cpp

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,56 @@ RewriteSystem::computeCriticalPair(ArrayRef<Symbol>::const_iterator from,
212212
return false;
213213
}
214214

215-
// Add the pair (XV, TY).
216-
pairs.emplace_back(xv, ty, path);
215+
// If Y == UW for some W, then the critical pair is (XV, TUW),
216+
// and we have
217+
// - lhs == (TU -> X)
218+
// - rhs == (UV -> UW).
219+
//
220+
// We explicitly apply the rewrite step (TU = X) to the rewrite path,
221+
// transforming the critical pair to (XV, XW).
222+
//
223+
// In particular, if T == X, U == [P] for some protocol P, and
224+
// V == W.[p] for some property symbol p, then we in fact have a pair
225+
// of property rules:
226+
//
227+
// - lhs == (T.[P] => T)
228+
// - rhs == ([P].W.[p] => [P].W)
229+
//
230+
// Without this hack, the critical pair would be:
231+
//
232+
// (T.w.[p] => T.[P].w)
233+
//
234+
// With this hack, the critical pair becomes:
235+
//
236+
// (T.w.[p] => T.w)
237+
//
238+
// This ensures that the newly-added rule is itself a property rule;
239+
// otherwise, this would only be the case if addRule() reduced T.[P].w
240+
// into T.w without immediately reducing some subterm of T first.
241+
//
242+
// While completion will eventually simplify all such rules down into
243+
// property rules, their existance in the first place breaks subtle
244+
// invariants in the minimal conformances algorithm, which expects
245+
// homotopy generators describing redundant protocol conformance rules
246+
// to have a certain structure.
247+
if (lhs.getLHS().size() <= ty.size() &&
248+
std::equal(lhs.getLHS().begin(),
249+
lhs.getLHS().end(),
250+
ty.begin())) {
251+
unsigned endOffset = ty.size() - lhs.getLHS().size();
252+
path.add(RewriteStep::forRewriteRule(/*startOffset=*/0,
253+
endOffset,
254+
getRuleID(lhs),
255+
/*inverse=*/false));
256+
257+
// Compute the term XW.
258+
MutableTerm xw(lhs.getRHS());
259+
xw.append(ty.end() - endOffset, ty.end());
260+
261+
pairs.emplace_back(xv, xw, path);
262+
} else {
263+
pairs.emplace_back(xv, ty, path);
264+
}
217265
}
218266

219267
return true;

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,10 @@ void RewriteSystem::simplifyRightHandSides() {
563563

564564
unsigned newRuleID = Rules.size();
565565

566+
if (Debug.contains(DebugFlags::Add)) {
567+
llvm::dbgs() << "## RHS simplification adds a rule " << lhs << " => " << rhs << "\n\n";
568+
}
569+
566570
// Add a new rule with the simplified right hand side.
567571
Rules.emplace_back(lhs, Term::get(rhs, Context));
568572
auto oldRuleID = Trie.insert(lhs.begin(), lhs.end(), newRuleID);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s
2+
3+
public protocol P1 {
4+
associatedtype T
5+
}
6+
7+
public protocol P2 : P1 {}
8+
9+
public protocol P3 : P2 {}
10+
11+
public protocol Q1 {}
12+
13+
public protocol Q2 : Q1 {}
14+
15+
public protocol Q3 : Q1 {}
16+
17+
public protocol R {
18+
associatedtype U: Q2, Q3
19+
}
20+
21+
struct G<X: P3, Y: R> where X.T == Y.U {
22+
// CHECK-LABEL: .G.Nested0@
23+
// CHECK-NEXT: <X, Y, Z where X : P3, Y : R, X.[P1]T == Y.[R]U>
24+
struct Nested0<Z> {}
25+
26+
// CHECK-LABEL: .G.Nested1@
27+
// CHECK-NEXT: <X, Y, Z where X : P3, Y : R, X.[P1]T == Y.[R]U>
28+
struct Nested1<Z> where X.T : Q1 {}
29+
30+
// CHECK-LABEL: .G.Nested2@
31+
// CHECK-NEXT: <X, Y, Z where X : P3, Y : R, X.[P1]T == Y.[R]U>
32+
struct Nested2<Z> where X.T : Q2 {}
33+
34+
// CHECK-LABEL: .G.Nested3@
35+
// CHECK-NEXT: <X, Y, Z where X : P3, Y : R, X.[P1]T == Y.[R]U>
36+
struct Nested3<Z> where X.T : Q3 {}
37+
}

0 commit comments

Comments
 (0)