Commit Graph

873 Commits

Author SHA1 Message Date
Slava Pestov
5689d046d4 RequirementMachine: Always add a rule for the trivial [P].[P] => [P] conformance
Completion adds this rule when a same-type requirement equates the Self of P
with some other type parameter conforming to P; one example is something like this:

  protocol P {
    associatedtype T : P where T.T == Self
  }

The initial rewrite system looks like this:

  (1) [P].T => [P:T]
  (2) [P:T].[P] => [P:T]
  (3) [P:T].[P:T] => [P]

Rules (2) and (3) overlap on the term

  [P:T].[P:T].[P]

Simplifying the overlapped term with rule (2) produces

  [P:T].[P:T]

Which further reduces to

  [P]

Simplifying the overlapped term with rule (3) produces

  [P].[P]

So we get a new rule

  [P].[P] => [P]

This is not a "real" conformance rule, and homotopy reduction wastes work
unraveling it in rewrite loops where this rule occurs. Instead, it's better
to introduce it as a permanent rule that is not subject to homotopy reduction
for every protocol.
2021-10-27 00:34:04 -04:00
Slava Pestov
6d89b42462 RequirementMachine: Simplify concrete substitutions when adding a new rule
Suppose we have these rules:

  (1) [P].[P] => [P]
  (2) [P].[P:X] => [P:X]
  (3) [P].[P:Y] => [P:Y]
  (4) [P:X].[concrete: G<τ_0_0> with <[P:Y]>]

Rule (2) and (4) overlap on the following term, which has had the concrete
type adjustment applied:

  [P].[P:X].[concrete: G<τ_0_0> with <[P].[P:Y]>]

The critical pair is obtained by applying rule (2) to both sides of the
branching is

  [P:X].[concrete: G<τ_0_0> with <[P].[P:Y]>] => [P:X]

Note that this is a distinct rule from (4), and again this new rule overlaps
with (2), producing another rule

  [P:X].[concrete: G<τ_0_0> with <[P].[P].[P:Y]>] => [P:X]

This process doesn't terminate. The root cause of this problem is the
existence of rule (1), which appears when there are multiple non-trivial
conformance paths witnessing the conformance Self : P. This occurs when a
same-type requirement is defined between Self and some other type conforming
to P.

To make this work, we need to simplify concrete substitutions when adding a
new rule in completion. Now that rewrite paths can represent this form of
simplification, this is easy to add.
2021-10-27 00:29:08 -04:00
Slava Pestov
97ed28ac4f RequirementMachine: simplify() supports appending to an existing rewrite path
This is for convenience with the subsequent change to addRule().
2021-10-27 00:28:49 -04:00
Slava Pestov
b01e97f2c6 RequirementMachine: Rewrite steps are instructions for a two-stack machine
I need to simplify concrete substitutions when adding a rewrite rule, so
for example if X.Y => Z, we want to simplify

  A.[concrete: G<τ_0_0, τ_0_1> with <X.Y, X>] => A

to

  A.[concrete: G<τ_0_0, τ_0_1> with <Z, X>] => A

The requirement machine used to do this, but I took it out, because it
didn't fit with the rewrite path representation. However, I found a case
where this is needed so I need to bring it back.

Until now, a rewrite path was a composition of rewrite steps where each
step would transform a source term into a destination term.

The question then becomes, how do we represent concrete substitution
simplification with such a scheme.

One approach is to rework rewrite paths to a 'nested' representation,
where a new kind of rewrite step applies a sequence of rewrite paths to
the concrete substitution terms. Unfortunately this would complicate
memory management and require recursion when visiting the steps of a
rewrite path.

Another, simpler approach that I'm going with here is to generalize a
rewrite path to a stack machine program instead.

I'm adding two new kinds of rewrite steps which manipulate a pair of
stacks, called 'A' and 'B':

- Decompose, which takes a term ending in a superclass or concrete type
  symbol, and pushes each concrete substitution on the 'A' stack.

- A>B, which pops the top of the 'A' stack and pushes it onto the 'B'
  stack.

Since all rewrite steps are invertible, the inverse of the two new
step kinds are as follows:

- Compose, which pops a series of terms from the 'A' stack, and replaces
  the concrete substitutions in the term ending in a superclass or
  concrete type symbol underneath.

- B>A, which pops the top of the 'B' stack and pushes it onto the
  'B' stack.

Both Decompose and Compose take an operand, which is the number of
concrete substitutions to expect. This is encoded in the RuleID field
of RewriteStep.

The two existing rewrite steps ApplyRewriteRule and AdjustConcreteType
simply pop and push the term at the top of the 'A' stack.

Now, if addRule() wishes to transform

  A.[concrete: G<τ_0_0, τ_0_1> with <X.Y, X>] => A

into

  A.[concrete: G<τ_0_0, τ_0_1> with <Z, X>] => A

it can construct the rewrite path

  Decompose(2) ⊗ A>B ⊗ <<rewrite path from X.Y to Z>> ⊗ B>A ⊗ Compose(2)

This commit lays down the plumbing for these new rewrite steps, and
replaces the existing 'evaluation' walks over rewrite paths that
mutate a single MutableTerm with a new RewritePathEvaluator type, that
stores the two stacks.

The changes to addRule() are coming in a subsequent commit.
2021-10-27 00:11:16 -04:00
Slava Pestov
84f02eae49 RequirementMachine: Fix crash in simplifyRewriteSystem() with -debug-requirement-machine=completion
If we're not recording homotopy generators, we can't access
HomotopyGenerators.back() in the debug output.
2021-10-27 00:11:10 -04:00
Slava Pestov
bcf5e9794b RequirementMachine: Take concrete substitutions into account when checking completion depth limit
We didn't look at the length of terms appearing in concrete substitutions,
so runaway recursion there was only caught by the completion step limit
which takes much longer.
2021-10-27 00:10:40 -04:00
Slava Pestov
e0a33445ac RequirementMachine: Fold what remains of ProtocolGraph into RewriteSystemBuilder 2021-10-21 19:00:41 -04:00
Slava Pestov
caea4607ff RequirementMachine: Simplify ProtocolGraph 2021-10-21 19:00:41 -04:00
Slava Pestov
0571b65cb8 RequirementMachine: Move protocol linear order from ProtocolGraph to RewriteContext 2021-10-21 19:00:10 -04:00
Slava Pestov
941438d6c8 RequirementMachine: New linear order on associated type symbols
Previously we said that if P1 inherits from P2, then [P1:T] < [P2:T].

However, this didn't generalize to merged associated type symbols;
we always had [P1&P2:T] < [P3:T] even if P3 inherited from both P1
and P2.

This meant that the 'merge' operation did not preserve order, which
fired an assertion in the completion logic.

Fix this by generalizing the linear order to compare the 'support'
of protocols rather than the 'depth', where the 'support' is
defined as the size of the transitive closure of the set under
protocol inheritance.

Then, if you have something like

  protocol P1 {}
  protocol P2 {}
  protocol P3 : P1, P2 {}

The support of 'P1 & P2' is 2, and the support of 'P3' is 3,
therefore [P3:T] < [P1&P2:T] in this example.

Fixes <rdar://problem/83768458>.
2021-10-21 19:00:10 -04:00
Slava Pestov
d00ad536d4 RequirementMachine: Split off PropertyUnification.cpp from PropertyMap.cpp 2021-10-19 18:42:41 -04:00
Slava Pestov
5ea7a06a2b RequirementMachine: Fix small typos 2021-10-19 18:40:31 -04:00
Slava Pestov
cc833d98a5 RequirementMachine: Record loops from trivially-resolved critical pairs 2021-10-19 18:40:31 -04:00
Slava Pestov
cd8692466d RequirementMachine: Better simulation of GSB's 'connected components' malarkey 2021-10-15 15:13:22 -04:00
Slava Pestov
77b8dfeeb2 RequirementMachine: Homotopy reduction prefers to delete less-canonical rules
Instead of deleting lower numbered rules, compare the LHS, and if they're
equal, compare the RHS, then use that to pick the rule to delete.
2021-10-15 15:13:22 -04:00
Slava Pestov
f8fb412e77 RequirementMachine: Add Rule::compare() 2021-10-15 15:13:22 -04:00
Slava Pestov
3185fd8606 RequirementMachine: Introduce Term::compare() to complement MutableTerm::compare() 2021-10-15 15:13:22 -04:00
Slava Pestov
25718c7d8f RequirementMachine: Preliminary support for minimizing layout, superclass and concrete type requirements 2021-10-14 15:03:28 -04:00
Slava Pestov
56439726ab RequirementMachine: Move some code from PropertyMap.cpp to RewriteContext.cpp 2021-10-14 15:03:26 -04:00
Slava Pestov
11aecda9aa RequirementMachine: Only add a concrete type adjustment step if the symbol has substitutions 2021-10-14 15:03:08 -04:00
Slava Pestov
6441b8bd73 RequirementMachine: Extract some verification logic out of minimizeRewriteSystem() 2021-10-14 15:03:08 -04:00
Slava Pestov
a6cd78eeab RequirementMachine: Fix a typo in debug output and comment 2021-10-14 15:03:08 -04:00
Slava Pestov
36b9c795d4 RequirementMachine: Remove redundant call to recordHomotopyGenerator() 2021-10-10 22:47:22 -04:00
Slava Pestov
576845d1c5 RequirementMachine: Tighten up type witness recursion hack
When a type parameter is subject to a conformance requirement and a
concrete type requirement, the concrete type unification pass
recursively walks each nested type introduced by the conformance
requirement, and fixes it to the concrete type witness in the
concrete type conformance.

In general, this can produce an infinite sequence of concrete type
requirements. There are a couple of heuristics to "tie off" the
recursion.

One heuristic is that if a nested type of a concrete parent type is
exactly equal to the parent type, a same-type requirement is
introduced between the child and the parent, preventing concrete
type unification from recursing further.

This used to check for exact equality of types, but it is possible
for the type witness to be equal under canonical type equivalence,
but use a different spelling via same-type requirements for some
type parameter appearing in structural position.

Instead, canonicalize terms here, allowing recursion where the
type witness names a different spelling of the same type.

Fixes <rdar://problem/83894546>.
2021-10-10 00:26:14 -04:00
Slava Pestov
4d5e641b4e RequirementMachine: Store the rewrite system in the property map 2021-10-10 00:20:19 -04:00
Slava Pestov
3ee99d6619 RequirementMachine: NFC cleanups for generating conformances 2021-10-09 19:11:15 -04:00
Slava Pestov
286e91d447 RequirementMachine: Temporarily disable associated type merging when minimizing protocol signatures
Also while plumbing this through, don't record homotopy generators
unless we're minimizing a protocol signature, since they're not
used for anything else yet.
2021-10-09 19:11:14 -04:00
Slava Pestov
85dfbcdedb RequirementMachine: Move some code from RequirementMachine.cpp to RequirementMachineRequests.cpp 2021-10-09 19:11:14 -04:00
Slava Pestov
7244102b4d RequirementMachine: Wire up protocol requirement signature minimization 2021-10-09 19:11:14 -04:00
Slava Pestov
a4c2ad43ab RequirementMachine: Use ProtocolDecl::getProtocolDependencies()
When using the requirement machine to build protocol signatures,
we can't get the protocol's dependencies by looking at the
conformance requirements in it's requirement signature, because
we haven't computed the requirement signature yet.

Instead, get the dependencies via the recently-added
getStructuralRequirements() request.
2021-10-08 19:38:48 -04:00
Slava Pestov
a5e680015a Merge pull request #39646 from slavapestov/fix-rqm-reference-invalidation
RequirementMachine: Fix reference invalidation bug
2021-10-08 18:30:35 -04:00
Slava Pestov
94e9ab6713 RequirementMachine: Track parent paths when computing generating conformances 2021-10-08 14:28:27 -04:00
Slava Pestov
b47b2d3b03 RequirementMachine: Fix generating conformances 2021-10-08 14:28:27 -04:00
Slava Pestov
70233aceb7 RequirementMachine: Teach generating conformances to understand protocol refinement
For implementation reasons we want the requirement signature of a
protocol to directly include all protocol refinement relationships,
even if they can be derived via same-type requirements between Self
and some nested type.

Therefore, a protocol refinement rule [P].[Q] => [P] can only be
replaced with a generating conformance equation that consists
entirely of other conformance rules.

This exactly simulates the existing behavior of the GSB's redundant
requirements algorithm.
2021-10-08 14:28:27 -04:00
Slava Pestov
371366eb4b RequirementMachine: Assert that non-redundant rules are fully resolved 2021-10-08 14:28:27 -04:00
Slava Pestov
14708adf5c RequirementMachine: Move implementation of RequirementSignatureRequest out of Sema
For now, this still uses the GSB.
2021-10-08 14:28:27 -04:00
Slava Pestov
b8177995df RequirementMachine: Sort requirements and canonicalize same-type requirements 2021-10-08 14:28:27 -04:00
Slava Pestov
566971dbc2 RequirementMachine: Add some comments and assertions around state transitions 2021-10-08 14:28:27 -04:00
Slava Pestov
526feebb8d RequirementMachine: Fix reference invalidation bug
After forming a reference to an entry in a DenseMap, we have to be
careful not touch the reference after calling any code which might
re-entrantly invalidate the reference by mutating the DenseMap.

Fixes rdar://problem/83891298.
2021-10-07 22:38:01 -04:00
Slava Pestov
2335116aee Merge pull request #39606 from slavapestov/rqm-requirement-signature-minimization
RequirementMachine: More progress toward computing protocol requirement signatures
2021-10-06 13:19:08 -04:00
Slava Pestov
62de909801 RequirementMachine: Some comments 2021-10-06 00:20:18 -04:00
Slava Pestov
03dfa60edd RequirementMachine: Initial implementation of requirement minimization 2021-10-06 00:11:21 -04:00
Slava Pestov
760efcafd8 RequirementMachine: When building a requirement signature system, remember the protocols 2021-10-06 00:11:21 -04:00
Slava Pestov
acb12fa750 RequirementMachine: More complete implementation of findProtocolConformanceRules() 2021-10-06 00:11:21 -04:00
Slava Pestov
c3270742dc RequirementMachine: Compute strongly connected components from the protocol dependency graph 2021-10-05 21:30:57 -04:00
Slava Pestov
17e2d6a290 RequirementMachine: Avoid term->type->term round-trip in getConformanceAccessPath()
We would skip recording a conformance access path if the subject
type canonicalized to a concrete type, but this was incorrect.

The correct formulation is to use the _canonical anchor_ and not
the canonical type as the caching key; that is, we always want it
to be a type parameter, even if it is fixed to a concrete type,
because type parameters fixed to concrete types can appear in
the middle of conformance access paths, as the example in the
radar demonstrates.

Fixes rdar://problem/83687967.
2021-10-05 15:06:42 -04:00
Slava Pestov
9fb2897953 RequirementMachine: Make Terms work as DenseMap keys 2021-10-05 15:06:42 -04:00
Slava Pestov
e7b0b1b35d RequirementMachine: Right-hand side simplification produces more aesthetically-pleasing 3-cells 2021-10-01 00:57:44 -04:00
Slava Pestov
62c9347d3b RequirementMachine: Overhaul generating conformances algorithm 2021-10-01 00:57:44 -04:00
Slava Pestov
2db676d41a RequirementMachine: Homotopy reduction deletes rules with unresolved name symbols first 2021-10-01 00:57:44 -04:00