Skip to content

Commit 82a494e

Browse files
committed
RequirementMachine: Go back to doing three passes in homotopy reduction
First pass: - Eliminate rules with unresolved name symbols in LHS - Eliminate simplified non-conformance rules Second pass: - Eliminate non-minimal conformance rules Third pass: - Eliminate all other non-conformance rules If you have a concrete requirement with a non-canonical substitution, like A : P, A == G<B.T> We add an induced rule for the abstract type witness, A.T == B.T This rule has a rewrite path in terms of the previous two rules, but we want to try to eliminate the other two rules first. This new ordering ensures we eliminate the simplified rule A == G<B.T> and compute minimal conformances before we get around to considering A.T == B.T, which can no longer be eliminated by that point. If we eliminate A.T == B.T first, we end up with simplified concrete type and concrete conformance rules which cannot be eliminated: A.[concrete: G<B.T>] => A A.[concrete: G<B.T> : P] => A This changes the minimized signature in an incompatible way in two test cases. In Generics/rdar83308672.swift, the signature becomes equivalent to what the GSB used to output. In Generics/sr10532.swift, the GSB would output an invalid signature anyway, so it doesn't matter.
1 parent 3c9344c commit 82a494e

File tree

5 files changed

+125
-71
lines changed

5 files changed

+125
-71
lines changed

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 56 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -335,57 +335,6 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID,
335335
return true;
336336
}
337337

338-
/// Check if a rewrite rule is a candidate for deletion in this pass of the
339-
/// minimization algorithm.
340-
bool RewriteSystem::
341-
isCandidateForDeletion(unsigned ruleID,
342-
const llvm::DenseSet<unsigned> *redundantConformances) const {
343-
const auto &rule = getRule(ruleID);
344-
345-
// We should not find a rule that has already been marked redundant
346-
// here; it should have already been replaced with a rewrite path
347-
// in all homotopy generators.
348-
assert(!rule.isRedundant());
349-
350-
// Associated type introduction rules are 'permanent'. They're
351-
// not worth eliminating since they are re-added every time; it
352-
// is better to find other candidates to eliminate in the same
353-
// loop instead.
354-
if (rule.isPermanent())
355-
return false;
356-
357-
// Other rules involving unresolved name symbols are derived from an
358-
// associated type introduction rule together with a conformance rule.
359-
// They are eliminated in the first pass.
360-
if (rule.getLHS().containsUnresolvedSymbols())
361-
return true;
362-
363-
// Protocol conformance rules are eliminated via a different
364-
// algorithm which computes "minimal conformances". This runs between
365-
// two passes of homotopy reduction.
366-
//
367-
// The first pass skips protocol conformance rules.
368-
//
369-
// The second pass eliminates any protocol conformance rule which is
370-
// redundant according to both homotopy reduction and the minimal
371-
// conformances algorithm.
372-
//
373-
// Later on, we verify that any conformance redundant via minimal
374-
// conformances was also redundant via homotopy reduction. This
375-
// means that the set of minimal conformances is always a superset
376-
// (or equal to) of the set of minimal protocol conformance
377-
// requirements that homotopy reduction alone would produce.
378-
if (rule.isAnyConformanceRule()) {
379-
if (!redundantConformances)
380-
return false;
381-
382-
if (!redundantConformances->count(ruleID))
383-
return false;
384-
}
385-
386-
return true;
387-
}
388-
389338
/// Find a rule to delete by looking through all loops for rewrite rules appearing
390339
/// once in empty context. Returns a redundant rule to delete if one was found,
391340
/// otherwise returns None.
@@ -401,7 +350,7 @@ isCandidateForDeletion(unsigned ruleID,
401350
/// \p redundantConformances equal to the set of conformance rules that are
402351
/// not minimal conformances.
403352
Optional<unsigned> RewriteSystem::
404-
findRuleToDelete(const llvm::DenseSet<unsigned> *redundantConformances,
353+
findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn,
405354
RewritePath &replacementPath) {
406355
SmallVector<std::pair<unsigned, unsigned>, 2> redundancyCandidates;
407356
for (unsigned loopID : indices(Loops)) {
@@ -423,15 +372,28 @@ findRuleToDelete(const llvm::DenseSet<unsigned> *redundantConformances,
423372

424373
for (const auto &pair : redundancyCandidates) {
425374
unsigned ruleID = pair.second;
426-
if (!isCandidateForDeletion(ruleID, redundantConformances))
375+
const auto &rule = getRule(ruleID);
376+
377+
// We should not find a rule that has already been marked redundant
378+
// here; it should have already been replaced with a rewrite path
379+
// in all homotopy generators.
380+
assert(!rule.isRedundant());
381+
382+
// Associated type introduction rules are 'permanent'. They're
383+
// not worth eliminating since they are re-added every time; it
384+
// is better to find other candidates to eliminate in the same
385+
// loop instead.
386+
if (rule.isPermanent())
387+
continue;
388+
389+
if (!isRedundantRuleFn(ruleID))
427390
continue;
428391

429392
if (!found) {
430393
found = pair;
431394
continue;
432395
}
433396

434-
const auto &rule = getRule(ruleID);
435397
const auto &otherRule = getRule(found->second);
436398

437399
// Prefer to delete "less canonical" rules.
@@ -495,10 +457,10 @@ void RewriteSystem::deleteRule(unsigned ruleID,
495457
}
496458

497459
void RewriteSystem::performHomotopyReduction(
498-
const llvm::DenseSet<unsigned> *redundantConformances) {
460+
llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
499461
while (true) {
500462
RewritePath replacementPath;
501-
auto optRuleID = findRuleToDelete(redundantConformances,
463+
auto optRuleID = findRuleToDelete(isRedundantRuleFn,
502464
replacementPath);
503465

504466
// If no redundant rules remain which can be eliminated by this pass, stop.
@@ -524,8 +486,24 @@ void RewriteSystem::minimizeRewriteSystem() {
524486

525487
propagateExplicitBits();
526488

527-
// First pass: Eliminate all redundant rules that are not conformance rules.
528-
performHomotopyReduction(/*redundantConformances=*/nullptr);
489+
// First pass:
490+
// - Eliminate all simplified non-conformance rules.
491+
// - Eliminate all rules with unresolved symbols.
492+
performHomotopyReduction([&](unsigned ruleID) -> bool {
493+
const auto &rule = getRule(ruleID);
494+
495+
if (rule.isSimplified() &&
496+
!rule.isAnyConformanceRule())
497+
return true;
498+
499+
// Other rules involving unresolved name symbols are derived from an
500+
// associated type introduction rule together with a conformance rule.
501+
// They are eliminated in the first pass.
502+
if (rule.getLHS().containsUnresolvedSymbols())
503+
return true;
504+
505+
return false;
506+
});
529507

530508
// Now compute a set of minimal conformances.
531509
//
@@ -537,8 +515,26 @@ void RewriteSystem::minimizeRewriteSystem() {
537515
llvm::DenseSet<unsigned> redundantConformances;
538516
computeMinimalConformances(redundantConformances);
539517

540-
// Second pass: Eliminate all redundant conformance rules.
541-
performHomotopyReduction(/*redundantConformances=*/&redundantConformances);
518+
// Second pass: Eliminate all non-minimal conformance rules.
519+
performHomotopyReduction([&](unsigned ruleID) -> bool {
520+
const auto &rule = getRule(ruleID);
521+
522+
if (rule.isAnyConformanceRule() &&
523+
redundantConformances.count(ruleID))
524+
return true;
525+
526+
return false;
527+
});
528+
529+
// Third pass: Eliminate all other redundant non-conformance rules.
530+
performHomotopyReduction([&](unsigned ruleID) -> bool {
531+
const auto &rule = getRule(ruleID);
532+
533+
if (!rule.isAnyConformanceRule())
534+
return true;
535+
536+
return false;
537+
});
542538

543539
// Check invariants after homotopy reduction.
544540
verifyRewriteLoops();

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -427,18 +427,14 @@ class RewriteSystem final {
427427

428428
void propagateExplicitBits();
429429

430-
bool
431-
isCandidateForDeletion(unsigned ruleID,
432-
const llvm::DenseSet<unsigned> *redundantConformances) const;
433-
434430
Optional<unsigned>
435-
findRuleToDelete(const llvm::DenseSet<unsigned> *redundantConformances,
431+
findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn,
436432
RewritePath &replacementPath);
437433

438434
void deleteRule(unsigned ruleID, const RewritePath &replacementPath);
439435

440436
void performHomotopyReduction(
441-
const llvm::DenseSet<unsigned> *redundantConformances);
437+
llvm::function_ref<bool(unsigned)> isRedundantRuleFn);
442438

443439
void computeMinimalConformances(
444440
llvm::DenseSet<unsigned> &redundantConformances);
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
2+
3+
protocol P {
4+
associatedtype T
5+
}
6+
7+
struct G<T> : P {}
8+
9+
10+
protocol Q {
11+
associatedtype A : P
12+
}
13+
14+
protocol R {}
15+
16+
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q1@
17+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.A == G<Self.A.T>, Self.B : P, Self.A.T == Self.B.T>
18+
19+
// GSB: Non-canonical requirement
20+
protocol Q1 : Q {
21+
associatedtype B : P where A == G<B.T>
22+
}
23+
24+
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q1a@
25+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.A == G<Self.A.T>, Self.B : P, Self.A.T : R, Self.A.T == Self.B.T>
26+
27+
// GSB: Missing requirement
28+
protocol Q1a : Q {
29+
associatedtype B : P where A.T : R, A == G<B.T>
30+
}
31+
32+
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q1b@
33+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.A == G<Self.A.T>, Self.B : P, Self.A.T : R, Self.A.T == Self.B.T>
34+
35+
// GSB: Non-canonical requirement
36+
protocol Q1b : Q {
37+
associatedtype B : P where B.T : R, A == G<B.T>
38+
}
39+
40+
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q2@
41+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.A == G<Self.A.T>, Self.B : P, Self.A.T == Self.B.T>
42+
43+
// GSB: Missing requirement
44+
protocol Q2 : Q {
45+
associatedtype B : P where A.T == B.T, A == G<B.T>
46+
}
47+
48+
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q3@
49+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.A == G<Self.A.T>, Self.B : P, Self.A.T == Self.B.T>
50+
51+
// GSB: Unsupported recursive requirement
52+
protocol Q3 : Q {
53+
associatedtype B : P where A == G<A.T>, A.T == B.T
54+
}
55+
56+
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q4@
57+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.A == G<Self.A.T>, Self.B : P, Self.A.T == Self.B.T>
58+
59+
// GSB: Unsupported recursive requirement
60+
protocol Q4 : Q {
61+
associatedtype B : P where A.T == B.T, A == G<A.T>
62+
}

test/Generics/rdar83308672.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,25 @@ protocol B {
3232
// we can drop one requirement but not both.
3333

3434
// CHECK: rdar83308672.(file).G1@
35-
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T : B>
35+
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T.X == Self.T.Y>
3636
protocol G1 {
3737
associatedtype T : A where T.X == T.Y
3838
}
3939

4040
// CHECK: rdar83308672.(file).G2@
41-
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T : B>
41+
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T.X == Self.T.Y>
4242
protocol G2 {
4343
associatedtype T : A where T : B, T.X == T.Y
4444
}
4545

4646
// CHECK: rdar83308672.(file).G3@
47-
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T : B>
47+
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T.X == Self.T.Y>
4848
protocol G3 {
4949
associatedtype T : A where T.X == T.Y, T : B
5050
}
5151

5252
// CHECK: rdar83308672.(file).G4@
53-
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T : B>
53+
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T.X == Self.T.Y>
5454
protocol G4 {
5555
associatedtype T : A where T : B
5656
}

test/Generics/sr10532.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public protocol ScalarMultiplicative {
1515
public protocol MapReduceArithmetic : ScalarMultiplicative, Collection where Element : ScalarMultiplicative, Scalar == Element.Scalar { }
1616

1717
// CHECK: sr10532.(file).Tensor@
18-
// CHECK-NEXT: Requirement signature: <Self where Self : MapReduceArithmetic, Self.Element : BinaryFloatingPoint, Self.Element : ScalarProtocol>
18+
// CHECK-NEXT: Requirement signature: <Self where Self : MapReduceArithmetic, Self.Element : BinaryFloatingPoint, Self.Element == Self.Scalar>
1919
public protocol Tensor : MapReduceArithmetic where Scalar : BinaryFloatingPoint, Element == Scalar {
2020
var magnitude: Scalar { get set }
2121
}

0 commit comments

Comments
 (0)