Commit Graph

117 Commits

Author SHA1 Message Date
Slava Pestov
fa65fd0e05 RequirementMachine: Plumb protocol typealiases through minimization
Now that we can detect protocol typealias rules, collect and keep
track of them so that they can be recorded in protocol requirement
signatures.

For now, this is all NFC since nothing introduces such rules into
the rewrite system, except for invalid requirements which are
diagnosed anyway.
2022-02-13 00:24:23 -05:00
Slava Pestov
65fda8092f RequirementMachine: Small fix for debug output 2022-02-13 00:24:23 -05:00
Slava Pestov
d39c667e26 RequirementMachine: New elimination order based on projection count
We want to prefer to eliminate rules that are not concrete type rules,
unless non-concrete type rule in question is a projection. For example,
suppose that this rule comes from a protocol:

    T.T == G<T.U, T.V>

And these three rules are in our minimization domain:

    a) T.T == G<Int, W>
    b) T.U == Int
    c) T.V == W

Then a) implies both b) and c), and vice versa.

In this case, we want to keep T.U == Int and T.V == W, and eliminate
T.T == G<Int, W>.

T.T == G<Int, W> is concrete and T.V == W is not, but because T.V == W
is defined by a projection, we still prefer to eliminate T.T == G<Int, W>
over T.V == W.
2022-02-09 11:31:35 -05:00
Slava Pestov
aeceda3cfa RequirementMachine: Introduce RewriteLoop::getProjectionCount() 2022-02-09 10:45:09 -05:00
Slava Pestov
50f84cb4bf RequirementMachine: Tweak debug output 2022-02-09 00:26:04 -05:00
Slava Pestov
0d0bcb2ff1 RequirementMachine: Simplify the Symbol API for removal of merged associated types 2022-02-07 18:57:45 -05:00
Slava Pestov
2c355de71b RequirementMachine: Introduce RewriteStep::{Left,Right}ConcreteProjection 2022-02-07 08:20:59 -05:00
Slava Pestov
06d4770adb RequirementMachine: Teach homotopy reduction to better deal with incomparable rules 2022-02-07 08:20:59 -05:00
Slava Pestov
037dc98845 RequirementMachine: Generalize compare() methods to return None instead of asserting on incomparable symbols 2022-02-07 08:20:59 -05:00
Slava Pestov
7e4a5876d1 RequirementMachine: Introduce RewriteStep::DecomposeConcrete 2022-02-07 08:20:58 -05:00
Slava Pestov
d8aa79c5e5 RequirementMachine: Rename RewriteStep::AdjustConcreteType to ::PrefixSubstitutions 2022-02-07 08:20:58 -05:00
Slava Pestov
a11151a7e3 RequirementMachine: Fix up some comments 2022-02-04 22:49:28 -05:00
Slava Pestov
3790bba7a0 RequirementMachine: Verify loops immediately when they're added to aid debugging 2022-02-04 22:48:35 -05:00
Slava Pestov
04f9bce79a RequirementMachine: Treat LHS-simplified and RHS-simplified/substitution-simplified rules separately in minimization 2022-01-27 18:54:03 -05:00
Slava Pestov
106896228e RequirementMachine: Split up Rule::isSimplified() into three predicates
We have three simplification passes, give each one its own predicate:
- Left hand side simplification
- Right hand side simplification
- Substitution simplification

This is for debugging output and will also allow me to tighten up
some invariants.
2022-01-27 18:54:03 -05:00
Slava Pestov
f3b8bedf1f RequirementMachine: Fix assertion failure when conflicting rules are also redundant
We can end up with two redundant concrete type rules on the same
term, but we would crash in homotopy reduction because the rules
were incomparable due to the concrete type symbol at the end.

If one of them is conflicting though, we don't really care about
the homotopy reduction order, so skip the check if the other
rule was already marked conflicting.
2022-01-25 00:32:25 -05:00
Slava Pestov
bdbf50e1e3 RequirementMachine: Replace RewriteStep::SameTypeWitness with a relation 2022-01-13 19:14:52 -05:00
Slava Pestov
6064671abd RequirementMachine: Replace RewriteStep::ConcreteTypeWitness with relations 2022-01-13 18:01:15 -05:00
Slava Pestov
84e2a627ec RequirementMachine: Replace RewriteStep::AbstractTypeWitness with relations 2022-01-13 00:02:31 -05:00
Slava Pestov
abb85fb3ca RequirementMachine: Replace RewriteStep::{Superclass,Concrete}Conformance with relations 2022-01-13 00:02:31 -05:00
Slava Pestov
3d2492fe56 RequirementMachine: Rename RewriteStep::ApplyRewriteRule to RewriteStep::Rule 2022-01-13 00:02:31 -05:00
Slava Pestov
746f4a5a8f RequirementMachine: Throw out rewrite loops not part of the minimization domain
When minimizing a generic signature, we only care about loops
where the basepoint is a generic parameter symbol.

When minimizing protocol requirement signatures in a connected
component, we only care about loops where the basepoint is a
protocol symbol or associated type symbol whose protocol is
part of the connected component.

All other loops can be discarded since they do not encode
redundancies that are relevant to us.
2022-01-05 23:59:46 -05:00
Slava Pestov
66cf913d68 RequirementMachine: Minimization understands 'relations' among layout requirements
A superclass requirement 'T : C' implies a layout requirement
'T : AnyObject' (if the class is @objc) or 'T : _NativeObject'
(if the class is not @objc).

In the latter case, there might already be a 'T : AnyObject'
requirement, in which case the type parameter 'T' is subject
to two layout requirements:

    T : AnyObject
    T : _NativeObject

The second requirement implies the first however. To encode this
in the world of rewrite loops, we the notion of a 'relation'
between property symbols, and a 'Relation' rewrite step.

Here, the relation is that _NativeObject < AnyObject. Once this
relation is recorded, the Relation rewrite step will transform

    T.[layout: _NativeObject].[layout: AnyObject]

into

    T.[layout: _NativeObject]

and vice versa.

This rewrite step allows us to construct a rewrite loop which
makes the first rewrite rule redundant via the second.
2022-01-04 22:35:58 -05:00
Slava Pestov
7148aea7ed RequirementMachine: Rename RewriteStep::RuleID to RewriteStep::Arg
It's used to store more than just the rule ID. However, add a
getRuleID() method that also asserts that Kind is ApplyRewriteRule
for readability.
2022-01-04 22:35:58 -05:00
Slava Pestov
82a494e31b 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.
2021-12-17 10:04:52 -05:00
Slava Pestov
3c9344cc80 RequirementMachine: Improved verifyMinimizedRules() 2021-12-17 10:04:52 -05:00
Slava Pestov
2bca132380 RequirementMachine: Introduce RewriteStep::AbstractTypeWitness 2021-12-17 10:04:52 -05:00
Slava Pestov
317743a487 RequirementMachine: Rename GeneratingConformances => MinimalConformances 2021-12-17 10:04:52 -05:00
Slava Pestov
d72500db0d RequirementMachine: Cache result of RewriteLoop::findRulesAppearingOnceInEmptyContext() 2021-12-17 10:04:51 -05:00
Slava Pestov
7708c75184 RequirementMachine: Introduce RewriteStep::SameTypeWitness 2021-12-14 02:16:46 -05:00
Slava Pestov
654953cbac RequirementMachine: Introduce RewriteStep::ConcreteTypeWitness 2021-12-14 02:16:46 -05:00
Slava Pestov
7a56ab8c18 RequirementMachine: Mark conflicting rules
This isn't quite right, because we really want the longer (more
specific) of the two rules to be conflicting, not the most-recently
added rule.
2021-12-13 18:48:23 -05:00
Slava Pestov
86c15ad5c1 RequirementMachine: Generating conformances can reason about concrete conformances now 2021-12-08 00:53:35 -05:00
Slava Pestov
47f3341b40 RequirementMachine: Delete loops that don't have rules in empty context
When a rule is replaced by a rewrite path, if the rule is in context
then every step of the replacement step is necessarily always in context.

The only way new rules in empty context can be introduced by a
replacement is if the original rewrite step being replaced had no
context.

Therefore, loops that do not contain rules in empty context will
never provide any additional information during homotopy reduction.
2021-12-08 00:53:35 -05:00
Slava Pestov
1bf6102f1e RequirementMachine: Don't bother normalizing loops 2021-12-08 00:53:35 -05:00
Slava Pestov
34cbfd23a5 RequirementMachine: Don't assert if a rewrite system has unresolved rules
This is a source-level error, not an invariant violation. Instead, plumb
a new hadError() flag, which in the future will assert if no diagnostic
was produced.
2021-12-08 00:53:34 -05:00
Slava Pestov
7427b68785 RequirementMachine: Introduce RewriteStep::ConcreteConformance and ::SuperclassConformance 2021-12-06 23:04:46 -05:00
Slava Pestov
e9b94c1bf1 RequirementMachine: Move some methods from RewriteStep to RewritePathEvaluator
This better encapsulates the evaluator state into a single struct.
2021-12-06 23:04:46 -05:00
Saleem Abdulrasool
910fbee14e gardening: make c++98-compat-extra-semi an error
This cleans up 90 instances of this warning and reduces the build spew
when building on Linux.  This helps identify actual issues when
building which can get lost in the stream of warning messages.  It also
helps restore the ability to build the compiler with gcc.
2021-11-27 11:40:17 -08: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
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
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
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
745acea7ce RequirementMachine: Introduce Rule::isIdentityConformanceRule() 2021-10-27 00:39:07 -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
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