Commit Graph

859 Commits

Author SHA1 Message Date
Slava Pestov
5210e5d475 RequirementMachine: Remove some debugging code 2021-09-24 08:59:51 -04:00
Slava Pestov
99dab70840 RequirementMachine: Mark associated type introduction rules permanent 2021-09-24 08:59:51 -04:00
Slava Pestov
8fbe93c469 RequirementMachine: Rename Rule::isDeleted() => isSimplified(), and add isPermanent(), isRedundant() bits 2021-09-24 08:59:51 -04:00
Slava Pestov
1fa36c70c6 RequirementMachine: Factor out Rule::isPropertyRule() from the property map 2021-09-24 08:59:51 -04:00
Slava Pestov
67fad897ab RequirementMachine: Compute left canonical forms of paths 2021-09-24 08:59:51 -04:00
Slava Pestov
5f03c737d2 RequirementMachine: Preliminary implementation of homotopy reduction 2021-09-24 08:59:50 -04:00
Slava Pestov
c6403e65e1 RequirementMachine: RewriteStep stores both whiskers 2021-09-24 08:59:50 -04:00
Slava Pestov
5cbfbae536 RequirementMachine: Represent concrete type adjustment in a rewrite path 2021-09-24 08:59:50 -04:00
Slava Pestov
b317a5c5ee RequirementMachine: Verify homotopy generators 2021-09-24 08:59:50 -04:00
Slava Pestov
a75327e6b1 RequirementMachine: Record homotopy generators when adding new rules during completion 2021-09-24 08:59:50 -04:00
Slava Pestov
65e9dd1161 RequirementMachine: Trie.insert() updates existing entry 2021-09-24 08:59:50 -04:00
Slava Pestov
866a04905b RequirementMachine: When simplifying right hand sides, add new rules instead of mutating
We need to keep the original rules around for homotopy generators.
2021-09-24 08:59:50 -04:00
Slava Pestov
b2a21b3117 RequirementMachine: Preliminary plumbing for computing homotopy generators 2021-09-24 08:59:50 -04:00
Slava Pestov
e4f6128990 RequirementMachine: Don't simplify terms inside concrete substitutions when adding a rule
The effect of doing this is difficult to represent with homotopy generators,
so let's just not bother.
2021-09-24 08:59:50 -04:00
Slava Pestov
0309cfa3d6 RequirementMachine: Reduction order on symbols should compare associated type name before protocols
The canonical order on associated types compares the name before the
protocol, so for example T.[P2:A] < T.[P1:B]. Make sure the reduction
order does the same so that we correctly compute canonical types in
this case.
2021-09-09 23:40:28 -04:00
Slava Pestov
9fac25d65b RequirementMachine: Clean up merged associated type algorithm some more
The left and right hand side of a merged associated type candidate rule
have a common prefix except for the associated type symbol at the end.

Instead of passing two MutableTerms, we can pass a single uniqued
Term and a Symbol.

Also, we can compute the merged symbol before pushing the candidate
onto the vector. This avoids unnecessary work if the merged symbol
is equal to the right hand side's symbol.
2021-09-02 23:02:52 -04:00
Slava Pestov
62f56d7c13 RequirementMachine: Skip trivial overlaps
If resolving an overlap produces a pair where both sides are equal,
we don't have to reduce both sides before throwing it out.
2021-09-02 22:57:10 -04:00
Slava Pestov
463d591496 RequirementMachine: Verify that generic parameter symbols are valid within the current signature 2021-08-26 02:07:18 -04:00
Slava Pestov
e29b081939 RequirementMachine: Dump conformance access paths 2021-08-26 02:07:18 -04:00
Slava Pestov
c09259961b RequirementMachine: Skip associated type merge candidate if we're not going to get anything new
If we have a rule

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

And Q inherits from P, then the merged symbol is [Q:A] and not [P&Q:A].
In this case, the merge operation won't produce any new rules, so we can
just skip it entirely.
2021-08-20 01:29:22 -04:00
Slava Pestov
6ec2aad857 RequirementMachine: Don't add unnecessary rule in merged associated types step
If we have a rewrite rule of the form

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

We introduce a pair of rules

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

But in reality only the second one is necessary. The first one is redundant
because you obtain the same result by applying the original rule followed by
the second rule.
2021-08-20 01:29:22 -04:00
Slava Pestov
7de1d9b9a4 RequirementMachine: Better debug output 2021-08-20 01:29:22 -04:00
Slava Pestov
399a600e32 RequirementMachine: Add -debug-requirement-machine= flag to control debug output 2021-08-20 01:29:22 -04:00
Slava Pestov
fe2f42bc72 RequirementMachine: Rename -debug-requirement-machine flag to -dump-requirement-machine 2021-08-19 22:14:58 -04:00
Slava Pestov
0935952038 RequirementMachine: Cache result of mergeAssociatedTypes() 2021-08-19 22:14:58 -04:00
Slava Pestov
eec233507b RequirementMachine: Cache the mapping from associated type symbols to associated type declarations 2021-08-13 17:27:49 -04:00
Slava Pestov
0602a2ede3 RequirementMachine: Simplify right hand sides on each iteration of completion 2021-08-13 16:34:32 -04:00
Slava Pestov
0766c1c460 RequirementMachine: Use a trie to speed up merged associated types pass 2021-08-13 16:34:32 -04:00
Slava Pestov
af05377afa RequirementMachine: Use trie to find overlapping rules 2021-08-13 16:34:27 -04:00
Slava Pestov
b3deddb68d AST: Factor out duplicated definition of compareAssociatedTypes() 2021-08-11 12:01:07 -04:00
Slava Pestov
2fc6ec551b RequirementMachine: Use trie for left hand side simplification
In a confluent rewrite system, if the left hand side of a rule
X => Y can be reduced by some other rule X' => Y', then it is
permissible to delete the original rule X => Y altogether.

Confluence means that rewrite rules can be applied in any
order, so it is always valid to apply X' => Y' first, thus
X => Y is obsolete.

This was previously done in the completion procedure via a
quadratic algorithm that attempted to reduce each existing
rule via the newly-added rule obtained by resolving a critical
pair. Instead, we can do this in the post-processing pass
where we reduce right hand sides using a trie to speed up
the lookup.

This increases the amount of work performed by the
completion procedure, but eliminates the quadratic algorithm,
saving time overall.
2021-08-07 01:51:31 -04:00
Slava Pestov
9738d4eef6 RequirementMachine: PropertyMap can use Terms instead of MutableTerms as keys
Also, we don't have to sort rules in term order before adding them to
the map; a bucket sort by term length is sufficient.
2021-08-06 14:17:20 -04:00
Slava Pestov
47fc3d87ad RequirementMachine: Speed up PropertyMap lookups with a suffix trie
Whereas term simplification uses a prefix trie with shortest matching,
the PropertyMap uses a suffix trie with longest matching.
2021-08-06 14:17:20 -04:00
Slava Pestov
776a0889b7 RequirementMachine: Parametrize Trie by value type and add support for longest-suffix match
The PropertyMap wants to use a try to map to PropertyBags, and it
needs longest-suffix rather than shortest-prefix matching.

Implement both by making Trie into a template class with two
parameters; the ValueType, and the MatchKind.

Note that while the MatchKind encodes the longest vs shortest
match part, matching on the prefix vs suffix of a term is up
to the caller, since the find() and insert() methods of Trie
take a pair of iterators, so simply passing in begin()/end() vs
rbegin()/rend() selects the direction.
2021-08-06 14:17:20 -04:00
Slava Pestov
156fa2cc04 RequirementMachine: Speed up term simplification with a prefix trie
Previously RewriteSystem::simplify() would attempt to apply every
rewrite rule at every position in the original term, which was
obviously a source of overhead.

The trie itself is somewhat unoptimized; for example, with a bit of
effort it could merge a node with its only child, if nodes stored
a range of elements to compare rather than a single element.
2021-08-05 21:42:50 -04:00
Slava Pestov
324b83d4b4 RequirementMachine: Fix a typo in a comment 2021-08-05 21:42:50 -04:00
Slava Pestov
64330f5e79 RequirementMachine: Iterate over a vector in reverse, instead of reversing it first 2021-08-05 21:42:50 -04:00
Slava Pestov
65f27d3ab7 RequirementMachine: Fix subtle corner case where we could insert duplicate rewrite rules
RewriteSystem::addRule() did two things before adding the rewrite rule:

- Simplify both sides. If both sides are now equal, discard the rule.

- If the last symbol on the left hand side was a superclass or concrete type
  symbol, simplify the substitution terms in that symbol.

The problem is that the second step can produce a term which can be further
simplified, and in particular, one that is exactly equal to the left hand
side of some other rule.

To fix this, swap the order of the two steps. The only wrinkle is now we
have to check for a concrete type symbol at the end of _both_ the left hand
side and right hand side, since we don't orient the rule until we simplify
both sides.

I don't have a reduced test case for this one, but it was revealed by
compiler_crashers_2_fixed/0109-sr4737.swift after I introduced the trie.
2021-08-05 21:42:50 -04:00
Slava Pestov
bc398ae1df RequirementMachine: Make Symbols hashable 2021-08-05 21:42:50 -04:00
Slava Pestov
92ac06a25b RequirementMachine: Rules store uniqued Terms 2021-08-05 21:42:50 -04:00
Slava Pestov
ed966e7337 RequirementMachine: Add histograms for symbol kinds and term length 2021-08-05 21:42:50 -04:00
Slava Pestov
b7c4d8205c RequirementMachine: Add a simple Histogram data structure 2021-08-05 21:42:50 -04:00
Slava Pestov
fb5d180afd RequirementMachine: Superclass and concrete type symbols should not contain type variables 2021-08-05 21:42:49 -04:00
Slava Pestov
7adfd86e37 RequirementMachine: Split off Symbol.cpp and Term.cpp from RewriteSystem.cpp 2021-08-05 21:42:49 -04:00
Slava Pestov
66cacf7add RequirementMachine: Fix another bug in getTypeFromSubstitutionSchema()
protocol Q {
      associatedtype T where T == Self?
    }

    func foo<X, Y : Q>(_: X, _: Y) {}

This generates the rewrite system

    [Q:T].[concrete: Optional<τ_0_0> with <[Q]>]
    τ_0_1.[Q] => τ_0_1

With this property map:

    [Q:T] => { [concrete: Optional<τ_0_0> with <[Q]>] }
    τ_0_1 => { [Q] }

Suppose we're resolving the concrete type τ_0_1.[Q:T]. The property map
entry is keyed by [Q:T], so the prefix τ_0_1 must be prepended to the
concrete substitutions of [concrete: Optional<τ_0_0> with <[Q]>].

However, [Q] is just the protocol Self type, and τ_0_0.[Q] is not a
valid type-like term. We could simplify the term before building the
Swift type which would apply the second rewrite rule, but it's easier
to just drop the protocol symbol in this case.
2021-08-04 18:09:21 -04:00
Slava Pestov
4f2aa5f8d9 RequirementMachine: Implement superclass unification
Just as with concrete types, if we find that the same suffix has two
different superclass symbols in the property map, we need to introduce
same-type requirements between their generic parameters.

The added wrinkle is that the classes might be different; in this case,
one must be a superclass of the other, and we repeatedly substitute
the generic arguments until we get the generic arguments of the
superclass before we unify.
2021-08-04 01:24:00 -04:00
Slava Pestov
f0bec2935c RequirementMachine: Pull ConcreteTypeMatcher out of unifyConcreteTypes()
I'm going to use this to unify superclass types as well.
2021-08-04 01:24:00 -04:00
Slava Pestov
9eb87df3d7 AST: Generalize TypeMatcher::alwaysMismatchGenericParams() to alwaysMismatchTypeParameters() 2021-08-04 01:24:00 -04:00
Slava Pestov
f39a0a888f RequirementMachine: Use the correct predicate to determine if a class uses Objective-C reference counting
Also, introduce the layout requirement implied by a superclass requirement
when lowering requirements, instead of when building the property map.

Right now this is functionally equivalent, but if we ever start to
test properties by checking for joinability of T with T.[p] instead of
looking at the property map entry of T, this new formulation is the
right one.
2021-08-04 01:24:00 -04:00
Slava Pestov
9a8ee6017a RequirementMachine: Fix silly oversight when resolving concrete types and superclass bounds
The property map stores the concrete type or superclass bound for all
terms whose suffix is equal to some key, so eg if you have

  protocol P {
    associatedtype T where T == U?
    associatedtype U
  }

Then you have this rewrite system

  [P].T => [P:T]
  [P].U => [P:U]
  [P:T].[concrete: Optional<τ_0_0> with <[P:U]>] => [P:T]

Which creates this property map

  [P:T] => { [concrete: Optional<τ_0_0> with <[P:U]>] }

Now if I start with a generic signature like

  <T, U where U : P>

This produces the following rewrite system:

  τ_0_1.[U] => τ_0_1
  τ_0_1.T => τ_0_1.[P:T]
  τ_0_1.U => τ_0_1.[P:U]

Consider the computation of the canonical type of τ_0_1.T. This term
reduces to τ_0_1.[P:T]. The suffix [P:T] has a concrete type symbol in
the property map, [concrete: Optional<τ_0_0> with <[P:U]>].

However it would not be correct to canonicalize τ_0_1.[P:T] to
Optional<τ_0_0>.subst { τ_0_0 => getTypeForTerm([P:T]) }; this
produces the type Optional<τ_0_0.T>, and not Optional<τ_0_1.T> as
expected.

The reason is that the substitution τ_0_0 => getTypeForTerm([P:T])
is "relative" to the protocol Self type of P, since domain([P:T]) = {P}.

Indeed, the right solution here is to note that τ_0_1.[P:T] has a suffix
equal to the key of the property map entry, [P:T]. If we strip off the
suffix, we get τ_0_1. If we then prepend τ_0_1 to the substitution term,
we get the term τ_0_1.[P:U], whose canonical type is τ_0_1.U.

Now, applying the substitution τ_0_0 => τ_0_1.U to Optional<τ_0_0>
produces the desired result, Optional<τ_0_1.U>.

Note that this is the same "prepend a prefix to each substitution of
a concrete type symbol" operation that is performed in checkForOverlap()
and PropertyBag::copyPropertiesFrom(), however we can simplify things
slightly by open-coding it instead of calling the utility method
prependPrefixToConcreteSubstitutions(), since the latter creates a
new symbol, which we don't actually need.
2021-08-04 01:21:21 -04:00