Skip to content

Commit 6509eff

Browse files
authored
Merge pull request #41412 from slavapestov/rqm-preserve-redundant-rule-paths
RequirementMachine: Preserve replacement paths for redundant rules
2 parents 93585a9 + 6bcc526 commit 6509eff

18 files changed

+168
-64
lines changed

lib/AST/RequirementMachine/Debug.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ enum class DebugFlags : unsigned {
5959

6060
/// Print debug output from generic signature minimization.
6161
Minimization = (1<<12),
62+
63+
/// Print redundant rules and their replacement paths.
64+
RedundantRules = (1<<13),
65+
66+
/// Print more detail about redundant rules.
67+
RedundantRulesDetail = (1<<14)
6268
};
6369

6470
using DebugOptions = OptionSet<DebugFlags>;

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -420,11 +420,17 @@ findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
420420
// Homotopy reduction runs multiple passes with different filters to
421421
// prioritize the deletion of certain rules ahead of others. Apply
422422
// the filter now.
423-
if (!isRedundantRuleFn(ruleID))
423+
if (!isRedundantRuleFn(ruleID)) {
424+
if (Debug.contains(DebugFlags::HomotopyReductionDetail)) {
425+
llvm::dbgs() << "** Skipping rule " << rule << " from loop #"
426+
<< pair.first << "\n";
427+
}
428+
424429
continue;
430+
}
425431

426432
if (Debug.contains(DebugFlags::HomotopyReductionDetail)) {
427-
llvm::dbgs() << "** Candidate " << rule << " from loop #"
433+
llvm::dbgs() << "** Candidate rule " << rule << " from loop #"
428434
<< pair.first << "\n";
429435
}
430436

@@ -552,6 +558,12 @@ findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
552558
/// occurrences of the rule in all loops with the replacement path.
553559
void RewriteSystem::deleteRule(unsigned ruleID,
554560
const RewritePath &replacementPath) {
561+
// Replace all occurrences of the rule with the replacement path in
562+
// all redundant rule paths recorded so far.
563+
for (auto &pair : RedundantRules) {
564+
(void) pair.second.replaceRuleWithPath(ruleID, replacementPath);
565+
}
566+
555567
// Replace all occurrences of the rule with the replacement path in
556568
// all remaining rewrite loops.
557569
for (unsigned loopID : indices(Loops)) {
@@ -573,6 +585,9 @@ void RewriteSystem::deleteRule(unsigned ruleID,
573585
llvm::dbgs() << "\n";
574586
}
575587
}
588+
589+
// Record the redundant rule along with its replacement path.
590+
RedundantRules.emplace_back(ruleID, replacementPath);
576591
}
577592

578593
void RewriteSystem::performHomotopyReduction(
@@ -702,6 +717,26 @@ void RewriteSystem::minimizeRewriteSystem() {
702717
verifyRewriteLoops();
703718
verifyRedundantConformances(redundantConformances);
704719
verifyMinimizedRules(redundantConformances);
720+
721+
if (Debug.contains(DebugFlags::RedundantRules)) {
722+
llvm::dbgs() << "\nRedundant rules:\n";
723+
for (const auto &pair : RedundantRules) {
724+
const auto &rule = getRule(pair.first);
725+
llvm::dbgs() << "- " << rule << " ::== ";
726+
727+
MutableTerm lhs(rule.getLHS());
728+
pair.second.dump(llvm::dbgs(), lhs, *this);
729+
730+
llvm::dbgs() << "\n";
731+
732+
if (Debug.contains(DebugFlags::RedundantRulesDetail)) {
733+
llvm::dbgs() << "\n";
734+
pair.second.dumpLong(llvm::dbgs(), lhs, *this);
735+
736+
llvm::dbgs() << "\n\n";
737+
}
738+
}
739+
}
705740
}
706741

707742
/// In a conformance-valid rewrite system, any rule with unresolved symbols on
@@ -826,6 +861,8 @@ void RewriteSystem::verifyRedundantConformances(
826861
void RewriteSystem::verifyMinimizedRules(
827862
const llvm::DenseSet<unsigned> &redundantConformances) const {
828863
#ifndef NDEBUG
864+
unsigned redundantRuleCount = 0;
865+
829866
for (unsigned ruleID : indices(Rules)) {
830867
const auto &rule = getRule(ruleID);
831868

@@ -845,8 +882,11 @@ void RewriteSystem::verifyMinimizedRules(
845882
continue;
846883
}
847884

885+
if (rule.isRedundant())
886+
++redundantRuleCount;
887+
848888
// LHS-simplified rules should be redundant, unless they're protocol
849-
// conformance rules, which unfortunately might no be redundant, because
889+
// conformance rules, which unfortunately might not be redundant, because
850890
// we try to keep them in the original protocol definition for
851891
// compatibility with the GenericSignatureBuilder's minimization algorithm.
852892
if (rule.isLHSSimplified() &&
@@ -877,5 +917,22 @@ void RewriteSystem::verifyMinimizedRules(
877917
abort();
878918
}
879919
}
920+
921+
if (RedundantRules.size() != redundantRuleCount) {
922+
llvm::errs() << "Expected " << RedundantRules.size() << " redundant rules "
923+
<< "but counted " << redundantRuleCount << "\n";
924+
dump(llvm::errs());
925+
abort();
926+
}
927+
928+
for (const auto &pair : RedundantRules) {
929+
const auto &rule = getRule(pair.first);
930+
if (!rule.isRedundant()) {
931+
llvm::errs() << "Recorded replacement path for non-redundant rule "
932+
<< rule << "\n";
933+
dump(llvm::errs());
934+
abort();
935+
}
936+
}
880937
#endif
881938
}

lib/AST/RequirementMachine/PropertyUnification.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,14 +269,14 @@ void PropertyMap::addSuperclassProperty(
269269
recordRelation(key, ruleID, layoutSymbol, System, debug);
270270
}
271271

272-
if (debug) {
273-
llvm::dbgs() << "% New superclass " << superclassDecl->getName()
274-
<< " for " << key << " is ";
275-
}
276-
277272
// If this is the first superclass requirement we've seen for this term,
278273
// just record it and we're done.
279274
if (!props->SuperclassDecl) {
275+
if (debug) {
276+
llvm::dbgs() << "% New superclass " << superclassDecl->getName()
277+
<< " for " << key << "\n";
278+
}
279+
280280
props->SuperclassDecl = superclassDecl;
281281

282282
assert(props->Superclasses.empty());
@@ -290,6 +290,11 @@ void PropertyMap::addSuperclassProperty(
290290
return;
291291
}
292292

293+
if (debug) {
294+
llvm::dbgs() << "% New superclass " << superclassDecl->getName()
295+
<< " for " << key << " is ";
296+
}
297+
293298
// Otherwise, we compare it against the existing superclass requirement.
294299
assert(!props->Superclasses.empty());
295300

lib/AST/RequirementMachine/RewriteContext.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ static DebugOptions parseDebugFlags(StringRef debugFlags) {
4040
.Case("minimal-conformances", DebugFlags::MinimalConformances)
4141
.Case("protocol-dependencies", DebugFlags::ProtocolDependencies)
4242
.Case("minimization", DebugFlags::Minimization)
43+
.Case("redundant-rules", DebugFlags::RedundantRules)
44+
.Case("redundant-rules-detail", DebugFlags::RedundantRulesDetail)
4345
.Default(None);
4446
if (!flag) {
4547
llvm::errs() << "Unknown debug flag in -debug-requirement-machine "

lib/AST/RequirementMachine/RewriteLoop.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,20 @@ void RewritePath::dump(llvm::raw_ostream &out,
188188
}
189189
}
190190

191+
void RewritePath::dumpLong(llvm::raw_ostream &out,
192+
MutableTerm term,
193+
const RewriteSystem &system) const {
194+
RewritePathEvaluator evaluator(term);
195+
196+
for (const auto &step : Steps) {
197+
evaluator.dump(out);
198+
evaluator.apply(step, system);
199+
out << "\n";
200+
}
201+
202+
evaluator.dump(out);
203+
}
204+
191205
void RewriteLoop::verify(const RewriteSystem &system) const {
192206
#ifndef NDEBUG
193207
RewritePathEvaluator evaluator(Basepoint);
@@ -220,13 +234,17 @@ void RewriteLoop::dump(llvm::raw_ostream &out,
220234
}
221235

222236
void RewritePathEvaluator::dump(llvm::raw_ostream &out) const {
223-
out << "Primary stack:\n";
224-
for (const auto &term : Primary) {
225-
out << term << "\n";
237+
for (unsigned i = 0, e = Primary.size(); i < e; ++i) {
238+
if (i == Primary.size() - 1)
239+
out << "-> ";
240+
else
241+
out << " ";
242+
243+
out << Primary[i] << "\n";
226244
}
227-
out << "\nSecondary stack:\n";
228-
for (const auto &term : Secondary) {
229-
out << term << "\n";
245+
246+
for (unsigned i = 0, e = Secondary.size(); i < e; ++i) {
247+
out << " " << Secondary[Secondary.size() - i - 1] << "\n";
230248
}
231249
}
232250

lib/AST/RequirementMachine/RewriteLoop.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,10 @@ class RewritePath {
409409
void dump(llvm::raw_ostream &out,
410410
MutableTerm term,
411411
const RewriteSystem &system) const;
412+
413+
void dumpLong(llvm::raw_ostream &out,
414+
MutableTerm term,
415+
const RewriteSystem &system) const;
412416
};
413417

414418
/// Information about protocol conformance rules appearing in a rewrite loop.

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,10 @@ class RewriteSystem final {
453453
/// algorithms.
454454
std::vector<RewriteLoop> Loops;
455455

456+
/// A list of pairs where the first element is a rule number and the second
457+
/// element is an equivalent rewrite path in terms of non-redundant rules.
458+
std::vector<std::pair<unsigned, RewritePath>> RedundantRules;
459+
456460
void propagateExplicitBits();
457461

458462
Optional<std::pair<unsigned, unsigned>>

lib/AST/RequirementMachine/Symbol.cpp

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -637,23 +637,25 @@ Symbol Symbol::transformConcreteSubstitutions(
637637

638638
/// Print the symbol using our mnemonic representation.
639639
void Symbol::dump(llvm::raw_ostream &out) const {
640-
auto dumpSubstitutions = [&]() {
641-
if (getSubstitutions().size() > 0) {
642-
out << " with <";
643-
644-
bool first = true;
645-
for (auto substitution : getSubstitutions()) {
646-
if (first) {
647-
first = false;
648-
} else {
649-
out << ", ";
650-
}
651-
substitution.dump(out);
652-
}
640+
llvm::DenseMap<CanType, Identifier> substitutionNames;
641+
if (hasSubstitutions()) {
642+
auto &ctx = getConcreteType()->getASTContext();
643+
644+
for (unsigned index : indices(getSubstitutions())) {
645+
Term substitution = getSubstitutions()[index];
646+
647+
std::string s;
648+
llvm::raw_string_ostream os(s);
649+
os << substitution;
653650

654-
out << ">";
651+
auto key = CanType(GenericTypeParamType::get(
652+
/*isTypeSequence=*/false, 0, index, ctx));
653+
substitutionNames[key] = ctx.getIdentifier(s);
655654
}
656-
};
655+
}
656+
657+
PrintOptions opts;
658+
opts.AlternativeTypeNames = &substitutionNames;
657659

658660
switch (getKind()) {
659661
case Kind::Name:
@@ -680,20 +682,20 @@ void Symbol::dump(llvm::raw_ostream &out) const {
680682
return;
681683

682684
case Kind::Superclass:
683-
out << "[superclass: " << getConcreteType();
684-
dumpSubstitutions();
685+
out << "[superclass: ";
686+
getConcreteType().print(out, opts);
685687
out << "]";
686688
return;
687689

688690
case Kind::ConcreteType:
689-
out << "[concrete: " << getConcreteType();
690-
dumpSubstitutions();
691+
out << "[concrete: ";
692+
getConcreteType().print(out, opts);
691693
out << "]";
692694
return;
693695

694696
case Kind::ConcreteConformance:
695-
out << "[concrete: " << getConcreteType();
696-
dumpSubstitutions();
697+
out << "[concrete: ";
698+
getConcreteType().print(out, opts);
697699
out << " : ";
698700
out << getProtocol()->getName();
699701
out << "]";

test/Generics/generic_objc_superclass.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ func foo<T : Generic<U>, U>(_: T, _: U) {
1313

1414
// CHECK-LABEL: Requirement machine for <τ_0_0, τ_0_1 where τ_0_0 : Generic<τ_0_1>>
1515
// CHECK-NEXT: Rewrite system: {
16-
// CHECK-NEXT: - τ_0_0.[superclass: Generic<τ_0_0> with <τ_0_1>] => τ_0_0
16+
// CHECK-NEXT: - τ_0_0.[superclass: Generic<τ_0_1>] => τ_0_0
1717
// CHECK-NEXT: - τ_0_0.[layout: AnyObject] => τ_0_0
1818
// CHECK-NEXT: }
1919
// CHECK: Property map: {
20-
// CHECK-NEXT: τ_0_0 => { layout: AnyObject superclass: [superclass: Generic<τ_0_0> with <τ_0_1>] }
20+
// CHECK-NEXT: τ_0_0 => { layout: AnyObject superclass: [superclass: Generic<τ_0_1>] }
2121
// CHECK-NEXT: }

test/Generics/infinite_concrete_type.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
class G<T> {}
44

55
protocol P1 { // expected-error {{cannot build rewrite system for protocol; concrete nesting limit exceeded}}
6-
// expected-note@-1 {{failed rewrite rule is [P1:A].[concrete: G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<τ_0_0>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> with <[P1:A]>] => [P1:A]}}
6+
// expected-note@-1 {{failed rewrite rule is [P1:A].[concrete: G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<[P1:A]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] => [P1:A]}}
77
associatedtype A where A == G<B>
88
associatedtype B where B == G<A>
99
}

test/Generics/non_confluent.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ struct S<U : P1> : P1 {
4545

4646
protocol P3 {
4747
// expected-error@-1 {{cannot build rewrite system for protocol; rule length limit exceeded}}
48-
// expected-note@-2 {{failed rewrite rule is [P3:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[concrete: S<S<S<S<S<S<S<S<S<S<S<S<τ_0_0>>>>>>>>>>>> with <[P3:U]>] => [P3:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T]}}
48+
// expected-note@-2 {{failed rewrite rule is [P3:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[concrete: S<S<S<S<S<S<S<S<S<S<S<S<[P3:U]>>>>>>>>>>>>] => [P3:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T]}}
4949

5050
associatedtype T : P1 where T == S<U>
5151
// expected-error@-1 {{type 'Self.U' does not conform to protocol 'P1'}}

test/Generics/sr12531.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=verify -requirement-machine-inferred-signatures=verify
22

33
protocol AnyPropertyProtocol {
44
associatedtype Root = Any
@@ -8,15 +8,21 @@ protocol AnyPropertyProtocol {
88
var value: Value { get }
99
}
1010

11+
// CHECK-LABEL: .PartialPropertyProtocol@
12+
// CHECK-NEXT: Requirement signature: <Self where Self : AnyPropertyProtocol, Self.[AnyPropertyProtocol]KP : PartialKeyPath<Self.[AnyPropertyProtocol]Root>
1113
protocol PartialPropertyProtocol: AnyPropertyProtocol
1214
where KP: PartialKeyPath<Root> {
1315
}
1416

17+
// CHECK-LABEL: .PropertyProtocol@
18+
// CHECK-NEXT: Requirement signature: <Self where Self : PartialPropertyProtocol, Self.[AnyPropertyProtocol]KP : WritableKeyPath<Self.[AnyPropertyProtocol]Root, Self.[AnyPropertyProtocol]Value>
1519
protocol PropertyProtocol: PartialPropertyProtocol
1620
where KP: WritableKeyPath<Root, Value> {
1721
}
1822

1923
extension Dictionary where Value: AnyPropertyProtocol {
24+
// CHECK-LABEL: .subscript@
25+
// CHECK-NEXT: Generic signature: <R, V, P where P : PropertyProtocol, P.[AnyPropertyProtocol]Root == R, P.[AnyPropertyProtocol]Value == V>
2026
subscript<R, V, P>(key: Key, path: WritableKeyPath<R, V>) -> P? where P: PropertyProtocol, P.Root == R, P.Value == V {
2127
return self[key] as? P
2228
}

test/Generics/unify_concrete_types_1.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct MergeTest<G : P1 & P2> {
2525
// CHECK: - τ_0_0.[P2:Z2] => τ_0_0.[P1:Z1]
2626
// CHECK: }
2727
// CHECK-LABEL: Property map: {
28-
// CHECK: [P1:X] => { concrete_type: [concrete: Foo<τ_0_0, τ_0_1> with <[P1:Y1], [P1:Z1]>] }
29-
// CHECK: [P2:X] => { concrete_type: [concrete: Foo<τ_0_0, τ_0_1> with <[P2:Y2], [P2:Z2]>] }
28+
// CHECK: [P1:X] => { concrete_type: [concrete: Foo<[P1:Y1], [P1:Z1]>] }
29+
// CHECK: [P2:X] => { concrete_type: [concrete: Foo<[P2:Y2], [P2:Z2]>] }
3030
// CHECK: τ_0_0 => { conforms_to: [P1 P2] }
3131
// CHECK: }

test/Generics/unify_nested_types_2.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct G<T : P1 & P2> {}
2525
// CHECK-LABEL: Adding generic signature <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> {
2626
// CHECK-LABEL: Rewrite system: {
2727
// CHECK: - [P1:T].T => [P1:T].[P1:T]
28-
// CHECK: - τ_0_0.[P1:T].[concrete: X<τ_0_0> with <τ_0_0.[P2:U]>] => τ_0_0.[P1:T]
28+
// CHECK: - τ_0_0.[P1:T].[concrete: X<τ_0_0.[P2:U]>] => τ_0_0.[P1:T]
2929
// CHECK: - τ_0_0.[P1:T].[P1:T] => τ_0_0.[P1:T]
3030
// CHECK: }
3131
// CHECK: }

test/Generics/unify_nested_types_3.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ struct G<T : P2a & P3a> {}
2828

2929
// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P2a, τ_0_0 : P3a>
3030
// CHECK-LABEL: Rewrite system: {
31-
// CHECK: - τ_0_0.[P2a:T].[concrete: X<τ_0_0> with <τ_0_0.[P3a:U]>] => τ_0_0.[P2a:T]
32-
// CHECK: - τ_0_0.[P2a:T].[P2:T].[concrete: X<τ_0_0> with <τ_0_0.[P3a:U].[P1:T]>] => τ_0_0.[P2a:T].[P2:T]
31+
// CHECK: - τ_0_0.[P2a:T].[concrete: X<τ_0_0.[P3a:U]>] => τ_0_0.[P2a:T]
32+
// CHECK: - τ_0_0.[P2a:T].[P2:T].[concrete: X<τ_0_0.[P3a:U].[P1:T]>] => τ_0_0.[P2a:T].[P2:T]
3333
// CHECK: }
3434
// CHECK-LABEL: Property map: {
35-
// CHECK: τ_0_0.[P2a:T] => { conforms_to: [P2] concrete_type: [concrete: X<τ_0_0> with <τ_0_0.[P3a:U]>] }
36-
// CHECK: τ_0_0.[P2a:T].[P2:T] => { concrete_type: [concrete: X<τ_0_0> with <τ_0_0.[P3a:U].[P1:T]>] }
35+
// CHECK: τ_0_0.[P2a:T] => { conforms_to: [P2] concrete_type: [concrete: X<τ_0_0.[P3a:U]>] }
36+
// CHECK: τ_0_0.[P2a:T].[P2:T] => { concrete_type: [concrete: X<τ_0_0.[P3a:U].[P1:T]>] }
3737
// CHECK: }
3838
// CHECK: }

0 commit comments

Comments
 (0)