Commit Graph

859 Commits

Author SHA1 Message Date
Slava Pestov
28c1178c4f RequirementMachine: Introduce TypeAliasRequirementsRequest
This is a verbatim copy of the GenericSignatureBuilder's somewhat
questionable (but necessary for source compatibility) logic where
protocol typealiases with the same name as some other associated
type imply a same-type requirement.

The related diagnostics are there too, but only emitted when
-requirement-machine-protocol-signatures=on; in 'verify' mode,
the GSB will emit the same diagnostics.
2021-11-19 15:48:01 -05:00
Slava Pestov
a208abc54c RequirementMachine: Desugar requirements in AbstractGenericSignatureRequest
The requirements passed to this request may have been substituted,
meaning the subject type might be a concrete type and not a type
parameter.

Also, the right hand side of conformance requirements here might be
a protocol composition.

Desugaring converts these kinds of requirements into "proper"
requirements where the subject type is always a type parameter,
which is what the RuleBuilder expects.
2021-11-12 14:30:46 -05:00
Slava Pestov
df8ce67539 RequirementMachine: Refactor requirement lowering 2021-11-12 14:30:46 -05:00
Slava Pestov
67535303b6 RequirementMachine: Move implementation of StructuralRequirementsRequest and ProtocolDependenciesRequest to RequirementLowering.cpp 2021-11-12 14:30:46 -05:00
Slava Pestov
ff5d0e1d63 RequirementMachine: Split off RuleBuilder into a new RequirementLowering.cpp file 2021-11-12 14:30:46 -05:00
Slava Pestov
d164fb8010 RequirementMachine: Rename RewriteSystemBuilder to RuleBuilder 2021-11-12 14:30:46 -05:00
Slava Pestov
2666449aa6 RequirementMachine: Implement AbstractGenericSignatureRequestRQM 2021-11-12 00:32:43 -05:00
Slava Pestov
24bdecdca5 AST: Remove ASTContext::getOrCreateRequirementMachine() in favor of getRewriteContext() 2021-11-11 22:39:20 -05:00
Slava Pestov
952dafad72 RequirementMachine: Preliminary refactoring in preparation for computing top-level generic signatures 2021-11-11 22:39:20 -05:00
Slava Pestov
796123eebd RequirementMachine: Avoid some expensive invariant checks in noassert builds 2021-11-11 22:39:20 -05:00
Slava Pestov
2dd14afc5e RequirementMachine: Minor comment fixes 2021-11-10 23:02:02 -05:00
Slava Pestov
2c4cfdcb1f RequirementMachine: Generating conformances prefers to delete non-explicit rules
This is a heuristic to ensure that conformance requirements remain in
their original protocol if possible, when the protocol is part of a
connected component consisting of multiple protocols.
2021-11-10 00:18:57 -05:00
Slava Pestov
dcefd70d11 RequirementMachine: Homotopy reduction propagates Rule::isExplicit() bit
If a rewrite loop contains two rules in empty context and one of them
is explicit, propagate the explicit bit to the other rule if the original
rule was found to be redundant.

This means that if we begin with an unresolved rule like

    [P].X.[Q] => [P].X [explicit]

And then completion added the resolved rule

    [P:X].[Q] => [P:X]

Then the new rule will become explicit once the original rule becomes
redundant.
2021-11-10 00:18:57 -05:00
Slava Pestov
8ab1c1f32e RequirementMachine: Remove one of the three homotopy reduction passes
This was a hack from before the change to minimize away the
least canonical rules first. Now, there's no reason to perform
a separate pass over rules containing unresolved name symbols.
2021-11-10 00:18:18 -05:00
Slava Pestov
15bf00148d RequirementMachine: Rename RewriteSystemCompletion.cpp to KnuthBendix.cpp 2021-11-10 00:18:18 -05:00
Slava Pestov
ecce9d2405 RequirementMachine: Narrower definition of Rule::isProtocolRefinementRule()
With this change the RequirementMachine's minimization behavior with
protocol refinement rules matches the GenericSignatureBuilder.

See https://github.com/apple/swift/pull/37466 for a full explanation
of what's going on here.
2021-11-10 00:18:18 -05:00
Slava Pestov
a4e848f598 RequirementMachine: New way of modeling layout requirement implied by superclass requirement
A superclass requirement implies a layout requirement. We don't
want the layout requirement to be present in the minimal
signature, so instead of adding a pair of requirements:

    T.[superclass: C<X, Y>] => T
    T.[layout: _NativeClass] => T

Add this pair of requirements:

    T.[superclass: C<X, Y>] => T
    [superclass: C<X, Y>].[layout: _NativeClass] => [superclass: C<X, Y>] [permanent]

Completion then derives the rule as a consequence:

    T.[layout: _NativeClass] => T

Since this rule is a consequence of the other two rules, homotopy
reduction will mark it redundant.
2021-11-10 00:18:18 -05:00
Slava Pestov
ba0fe6d7ac RequirementMachine: Add Rule::isExplicit() bit
Eventually I might upgrade this to a SourceLoc, for redundancy
diagnostics.
2021-11-10 00:18:18 -05:00
Slava Pestov
b7711454a2 RequirementMachine: Rename RewriteSystemBuilder::AssociatedTypeRules => PermanentRules 2021-11-10 00:18:18 -05:00
Robert Widmann
22405cefea Plumb the "Is Type Sequence" Bit Through the Surface AST 2021-11-08 13:48:30 -08:00
Slava Pestov
b30aa22f73 RequirementMachine: Split off RequirementSignatureRequestRQM from RequirementSignatureRequest
This is a refactoring needed to implement 'verify' mode. The
RequirementMachine computes the requirement signature for an
entire connected component of protocols at once, whereas the
GenericSignatureBuilder only does one protocol at a time.

Using the same request for both in 'verify' mode meant that
we would only call the GSB for the first protocol in a
connected component, and then the RequirementMachine would
fill in the rest.

To fix this, split it up into two requests. The original
RequirementSignatureRequest calls into the GSB, and then
kicks off a RequirementSignatureRequestRQM to get the
requirement signature computed by the RequirementMachine
(possibly cached, if this protocol is part of a connected
component with more than one protocol in it).
2021-11-01 22:51:59 -04:00
Slava Pestov
ddecb54ba4 RequirementMachine: Fix replaceRuleWithPath() to handle RewriteStep::Decompose
When re-contextualizing a path containing RewriteStep::Decompose,
don't update StartOffset/EndOffset for steps that execute with
more than one term on the stack.
2021-11-01 22:51:59 -04:00
Slava Pestov
49aaedeedb RequirementMachine: Fix RewriteStep::AdjustConcreteType
Using StartOffset to encode the prefix length is wrong, because
then we get an invalid rewrite step after replaceRuleWithPath().

Instead, encode the prefix length in the RuleID field.
2021-11-01 22:51:59 -04:00
Slava Pestov
5ac83e2819 RequirementMachine: Move buildPropertyMap() from RewriteSystem to PropertyMap
Now that the property map stores a reference to the RewriteSystem,
defining the buildPropertyMap() method on PropertyMap feels cleaner,
and allows more of the property map's internals to be private.
2021-11-01 22:51:26 -04:00
Slava Pestov
0d167a435c AST: Move GenericSignatureBuilder.h from include/swift/AST/ to lib/AST/ 2021-10-30 00:35:59 -04:00
Slava Pestov
5067bfefe4 RequirementMachine: Re-organize some methods in RewriteSystem.cpp 2021-10-27 01:28:24 -04:00
Slava Pestov
7d2b22a352 RequirementMachine: Fix runtime crash with MSVC
Signed vs unsigned bitfields strike again.
2021-10-27 01:28:24 -04:00
Slava Pestov
450c7c268a RequirementMachine: Split off RewriteLoop.cpp from HomotopyReduction.cpp 2021-10-27 01:28:24 -04:00
Slava Pestov
1057b56395 RequirementMachine: Rename HomotopyGenerator to RewriteLoop
Also, stop talking about 2-cells and 3-cells.
2021-10-27 01:28:24 -04:00
Slava Pestov
c0154d4d4e RequirementMachine: Try to delete less canonical conformance rules first
Just like in homotopy reduction, when finding a minimal set of generating
conformances, we should try to eliminate less canonical conformance rules
first.

This fixes a test case where we would delete a more canonical conformance
requirement, triggering an assertion that simplifed rules must be
redundant, because the less canonical conformance was simplified, whereas
the more canonical one was redundant and not simplified.
2021-10-27 01:28:24 -04:00
Slava Pestov
2687e93800 RequirementMachine: Proper handling of identity conformances when computing generating conformances
Consider this example:

  protocol P {
    associatedtype X : P where X == Self
  }

Clearly the conformance requirement 'X : P' is redundant, but previously
nothing in our formulation would make it so.

Here is the rewrite system:

  (1) [P:X].[P] => [P:X]
  (2) [P:X] => [P]

These two terms overlap on [P:X].[P]; resolving the critical pair introduces
the 'identity conformance' [P].[P] => [P]. Homotopy reduction would delete
this conformance, but at this point, [P:X].[P] => [P:X] would no longer be
redundant, since nothing else proves that [P].[P] => [P].

Now that [P].[P] => [P] is a permanent rule, we can handle this properly;
any conformance that appears in a rewrite loop together with an identity
conformance without context is completely redundant; it is equivalent to the
empty generating conformance path.
2021-10-27 00:44:26 -04:00
Slava Pestov
745acea7ce RequirementMachine: Introduce Rule::isIdentityConformanceRule() 2021-10-27 00:39:07 -04:00
Slava Pestov
f44926b6b9 RequirementMachine: Tighten up createRequirementFromRule()
Now that the 'identity conformance' [P].[P] => [P] is permanent,
we won't see it here, so we can just assume that any non-permanent,
non-redundant rule maps to a requirement.
2021-10-27 00:38:42 -04:00
Slava Pestov
a8bc8a86fb RequirementMachine: Add assertion to generating conformances algorithm 2021-10-27 00:38:35 -04:00
Slava Pestov
a729bebbc4 RequirementMachine: Improved rule deletion heuristic
Instead of finding the best redundancy candidate from the first
homotopy generator that has one, find the best redundancy
candidate from *all* homotopy generators that have one.

This correctly minimizes the "Joe Groff monoid" without hitting
the assertion guarding against simplified rules being non-redundant.

It also brings us closer to being able to correctly minimize
the protocol from https://bugs.swift.org/browse/SR-7353.
2021-10-27 00:38:33 -04:00
Slava Pestov
74d944d511 RequirementMachine: Use llvm::array_pod_sort() to sort requirements 2021-10-27 00:38:26 -04:00
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