Skip to content

Commit 5ac43b1

Browse files
committed
RequirementMachine: Fully canonicalize substitutions in concrete type rules
Previously we did this when adding new concrete type rules, but we don't have a complete rewrite system at that point yet, so there was no guarantee concrete substitution terms would be canonical. Now, perform simplification in a post-pass after completion, at the same time as simplifying rule right hand sides. Rewrite loops are recorded relating the original rule with the simplified substitutions.
1 parent e86fe27 commit 5ac43b1

8 files changed

+95
-21
lines changed

lib/AST/RequirementMachine/KnuthBendix.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
596596
}
597597
}
598598

599-
simplifyRewriteSystem();
599+
simplifyLeftHandSides();
600600

601601
assert(resolvedCriticalPairs.size() == resolvedPaths.size());
602602

@@ -629,6 +629,8 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
629629
resolvedPaths.clear();
630630
resolvedLoops.clear();
631631

632+
simplifyRightHandSidesAndSubstitutions();
633+
632634
// If the added rules merged any associated types, process the merges now
633635
// before we continue with the completion procedure. This is important
634636
// to perform incrementally since merging is required to repair confluence

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -251,14 +251,13 @@ bool RewriteSystem::simplify(MutableTerm &term, RewritePath *path) const {
251251

252252
/// Simplify terms appearing in the substitutions of the last symbol of \p term,
253253
/// which must be a superclass or concrete type symbol.
254-
void RewriteSystem::simplifySubstitutions(MutableTerm &term,
254+
bool RewriteSystem::simplifySubstitutions(Symbol &symbol,
255255
RewritePath &path) const {
256-
auto symbol = term.back();
257256
assert(symbol.hasSubstitutions());
258257

259258
auto substitutions = symbol.getSubstitutions();
260259
if (substitutions.empty())
261-
return;
260+
return false;
262261

263262
// Save the original rewrite path length so that we can reset if if we don't
264263
// find anything to simplify.
@@ -307,11 +306,12 @@ void RewriteSystem::simplifySubstitutions(MutableTerm &term,
307306
#endif
308307

309308
path.resize(oldSize);
310-
return;
309+
return false;
311310
}
312311

313312
// Build the new symbol with simplified substitutions.
314-
term.back() = symbol.withConcreteSubstitutions(newSubstitutions, Context);
313+
symbol = symbol.withConcreteSubstitutions(newSubstitutions, Context);
314+
return true;
315315
}
316316

317317
/// Adds a rewrite rule, returning true if the new rule was non-trivial.
@@ -341,12 +341,6 @@ bool RewriteSystem::addRule(MutableTerm lhs, MutableTerm rhs,
341341
RewritePath lhsPath;
342342
RewritePath rhsPath;
343343

344-
if (lhs.back().hasSubstitutions())
345-
simplifySubstitutions(lhs, lhsPath);
346-
347-
if (rhs.back().hasSubstitutions())
348-
simplifySubstitutions(rhs, rhsPath);
349-
350344
simplify(lhs, &lhsPath);
351345
simplify(rhs, &rhsPath);
352346

@@ -460,13 +454,11 @@ bool RewriteSystem::addExplicitRule(MutableTerm lhs, MutableTerm rhs) {
460454
return added;
461455
}
462456

463-
/// Delete any rules whose left hand sides can be reduced by other rules,
464-
/// and reduce the right hand sides of all remaining rules as much as
465-
/// possible.
457+
/// Delete any rules whose left hand sides can be reduced by other rules.
466458
///
467459
/// Must be run after the completion procedure, since the deletion of
468460
/// rules is only valid to perform if the rewrite system is confluent.
469-
void RewriteSystem::simplifyRewriteSystem() {
461+
void RewriteSystem::simplifyLeftHandSides() {
470462
assert(Complete);
471463

472464
for (unsigned ruleID = 0, e = Rules.size(); ruleID < e; ++ruleID) {
@@ -500,8 +492,19 @@ void RewriteSystem::simplifyRewriteSystem() {
500492
break;
501493
}
502494
}
495+
}
496+
}
497+
498+
/// Reduce the right hand sides of all remaining rules as much as
499+
/// possible.
500+
///
501+
/// Must be run after the completion procedure, since the deletion of
502+
/// rules is only valid to perform if the rewrite system is confluent.
503+
void RewriteSystem::simplifyRightHandSidesAndSubstitutions() {
504+
assert(Complete);
503505

504-
// If the rule was deleted above, skip the rest.
506+
for (unsigned ruleID = 0, e = Rules.size(); ruleID < e; ++ruleID) {
507+
auto &rule = getRule(ruleID);
505508
if (rule.isSimplified())
506509
continue;
507510

@@ -511,6 +514,8 @@ void RewriteSystem::simplifyRewriteSystem() {
511514
if (!simplify(rhs, &rhsPath))
512515
continue;
513516

517+
auto lhs = rule.getLHS();
518+
514519
// We're adding a new rule, so the old rule won't apply anymore.
515520
rule.markSimplified();
516521

@@ -544,6 +549,41 @@ void RewriteSystem::simplifyRewriteSystem() {
544549

545550
recordRewriteLoop(MutableTerm(lhs), loop);
546551
}
552+
553+
// Finally try to simplify substitutions in superclass, concrete type and
554+
// concrete conformance symbols.
555+
for (unsigned ruleID = 0, e = Rules.size(); ruleID < e; ++ruleID) {
556+
auto &rule = getRule(ruleID);
557+
if (rule.isSimplified())
558+
continue;
559+
560+
auto lhs = rule.getLHS();
561+
auto symbol = lhs.back();
562+
if (!symbol.hasSubstitutions())
563+
continue;
564+
565+
RewritePath path;
566+
567+
// (1) First, apply the original rule to produce the original lhs.
568+
path.add(RewriteStep::forRewriteRule(/*startOffset=*/0, /*endOffset=*/0,
569+
ruleID, /*inverse=*/true));
570+
571+
// (2) Now, simplify the substitutions to get the new lhs.
572+
if (!simplifySubstitutions(symbol, path))
573+
continue;
574+
575+
// We're either going to add a new rule or record an identity, so
576+
// mark the old rule as simplified.
577+
rule.markSimplified();
578+
579+
MutableTerm newLHS(lhs.begin(), lhs.end() - 1);
580+
newLHS.add(symbol);
581+
582+
// Invert the path to get a path from the new lhs to the old rhs.
583+
path.invert();
584+
585+
addRule(newLHS, MutableTerm(rule.getRHS()), &path);
586+
}
547587
}
548588

549589
void RewriteSystem::verifyRewriteRules(ValidityPolicy policy) const {

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ class RewriteSystem final {
278278

279279
bool simplify(MutableTerm &term, RewritePath *path=nullptr) const;
280280

281-
void simplifySubstitutions(MutableTerm &term, RewritePath &path) const;
281+
bool simplifySubstitutions(Symbol &symbol, RewritePath &path) const;
282282

283283
//////////////////////////////////////////////////////////////////////////////
284284
///
@@ -290,7 +290,9 @@ class RewriteSystem final {
290290
computeConfluentCompletion(unsigned maxIterations,
291291
unsigned maxDepth);
292292

293-
void simplifyRewriteSystem();
293+
void simplifyLeftHandSides();
294+
295+
void simplifyRightHandSidesAndSubstitutions();
294296

295297
enum ValidityPolicy {
296298
AllowInvalidRequirements,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
2+
3+
struct G<T> {}
4+
5+
// CHECK-LABEL: canonical_concrete_substitutions_in_protocol.(file).P@
6+
// CHECK-NEXT: Requirement signature: <Self where Self.A == G<Self.B>, Self.B == Self.T.X, Self.T : Q>
7+
8+
protocol P {
9+
associatedtype A where A == G<T.X>
10+
associatedtype B where B == T.X
11+
associatedtype T : Q
12+
}
13+
14+
protocol Q {
15+
associatedtype X
16+
}
17+
18+
protocol QQ : Q {}
19+
20+
protocol R {
21+
associatedtype A
22+
associatedtype C: QQ where C.X == G<A>
23+
}

test/Generics/unify_concrete_types_1.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ struct MergeTest<G : P1 & P2> {
2828
// CHECK: [P1:X] => { concrete_type: [concrete: Foo<τ_0_0, τ_0_1> with <[P1:Y1], [P1:Z1]>] }
2929
// CHECK: [P2:X] => { concrete_type: [concrete: Foo<τ_0_0, τ_0_1> with <[P2:Y2], [P2:Z2]>] }
3030
// CHECK: τ_0_0 => { conforms_to: [P1 P2] }
31-
// CHECK: τ_0_0.[P1&P2:X] => { concrete_type: [concrete: Foo<τ_0_0, τ_0_1> with <τ_0_0.[P2:Y2], τ_0_0.[P2:Z2]>] }
31+
// CHECK: τ_0_0.[P1&P2:X] => { concrete_type: [concrete: Foo<τ_0_0, τ_0_1> with <τ_0_0.[P1:Y1], τ_0_0.[P1:Z1]>] }
3232
// CHECK: }

test/Generics/unify_superclass_types_2.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ func unifySuperclassTest<T : P1 & P2>(_: T) {
3333
// CHECK: - τ_0_0.[P1&P2:X].[superclass: Generic<τ_0_0, String, τ_0_1> with <τ_0_0.[P2:A2], τ_0_0.[P2:B2]>] => τ_0_0.[P1&P2:X]
3434
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[layout: _NativeClass] => τ_0_0.[P1&P2:X]
3535
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[superclass: Generic<Int, τ_0_0, τ_0_1> with <τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] => τ_0_0.[P1&P2:X]
36+
// CHECK-NEXT: - τ_0_0.X => τ_0_0.[P1&P2:X]
37+
// CHECK-NEXT: - τ_0_0.[P2:X] => τ_0_0.[P1&P2:X]
3638
// CHECK-NEXT: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2]
3739
// CHECK-NEXT: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1]
3840
// CHECK-NEXT: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1]
@@ -43,7 +45,7 @@ func unifySuperclassTest<T : P1 & P2>(_: T) {
4345
// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Generic<Int, τ_0_0, τ_0_1> with <[P1:A1], [P1:B1]>] }
4446
// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<τ_0_0, String, τ_0_1> with <[P2:A2], [P2:B2]>] }
4547
// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] }
46-
// CHECK-NEXT: τ_0_0.[P1&P2:X] => { layout: _NativeClass superclass: [superclass: Generic<Int, τ_0_0, τ_0_1> with <τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] }
48+
// CHECK-NEXT: τ_0_0.[P1&P2:X] => { layout: _NativeClass superclass: [superclass: Generic<τ_0_0, String, τ_0_1> with <τ_0_0.[P2:A2], τ_0_0.[P1:B1]>] }
4749
// CHECK-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] }
4850
// CHECK-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] }
4951
// CHECK-NEXT: }

test/Generics/unify_superclass_types_3.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@ func unifySuperclassTest<T : P1 & P2>(_: T) {
3535
// CHECK: - τ_0_0.[P1&P2:X].[superclass: Generic<τ_0_0, String, τ_0_1> with <τ_0_0.[P2:A2], τ_0_0.[P2:B2]>] => τ_0_0.[P1&P2:X]
3636
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[layout: _NativeClass] => τ_0_0.[P1&P2:X]
3737
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[superclass: Derived<τ_0_0, τ_0_1> with <τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] => τ_0_0.[P1&P2:X]
38+
// CHECK-NEXT: - τ_0_0.X => τ_0_0.[P1&P2:X]
39+
// CHECK-NEXT: - τ_0_0.[P2:X] => τ_0_0.[P1&P2:X]
3840
// CHECK-NEXT: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2]
3941
// CHECK-NEXT: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1]
4042
// CHECK-NEXT: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1]
4143
// CHECK-NEXT: - τ_0_0.B2 => τ_0_0.[P1:B1]
44+
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[superclass: Generic<τ_0_0, String, τ_0_1> with <τ_0_0.[P2:A2], τ_0_0.[P1:B1]>] => τ_0_0.[P1&P2:X]
4245
// CHECK-NEXT: }
4346
// CHECK-NEXT: Rewrite loops: {
4447
// CHECK: }

test/Generics/unify_superclass_types_4.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ func unifySuperclassTest<T : P1 & P2>(_: T) {
3838
// CHECK: - τ_0_0.[P1&P2:X].[superclass: Derived<τ_0_0> with <τ_0_0.[P2:A2]>] => τ_0_0.[P1&P2:X]
3939
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[layout: _NativeClass] => τ_0_0.[P1&P2:X]
4040
// CHECK-NEXT: - τ_0_0.[P1&P2:X].[superclass: Base<τ_0_0> with <τ_0_0.[P1:A1]>] => τ_0_0.[P1&P2:X]
41+
// CHECK-NEXT: - τ_0_0.X => τ_0_0.[P1&P2:X]
42+
// CHECK-NEXT: - τ_0_0.[P2:X] => τ_0_0.[P1&P2:X]
4143
// CHECK-NEXT: - τ_0_0.[P2:A2].[Q:T] => τ_0_0.[P1:A1]
4244
// CHECK-NEXT: }
4345
// CHECK-NEXT: Rewrite loops: {

0 commit comments

Comments
 (0)