Commit Graph

859 Commits

Author SHA1 Message Date
Slava Pestov
3376372b7d RequirementMachine: Fix getCanonicalTypeInContext() to handle member types of superclass bounds
If you have

    protocol P {
      associatedtype T
    }

    class C<T> : P {}

Then substituting the type parameter T.T from the generic signature
<T where T : P> into the generic signature <T, U where T : C<U>>
is the identity operation, and also returns T.T, because subst()
isn't smart enough to replace T.T with U.

So getCanonicalTypeInContext() has to do the concrete conformance
lookup here, just like it does for the analogous situation where
you have a concrete type requirement.

This could be solved in a more principled way by better book-keeping
elsewhere, but the GSB supported the old behavior, so we can
simulate it easily enough in the RequirementMachine also.
2021-08-04 01:21:21 -04:00
Slava Pestov
c44b49e7e4 AST: Cache result of ProtocolDecl::getAssociatedTypeMembers() 2021-07-31 00:25:22 -04:00
Slava Pestov
1d7eae7bfb RequirementMachine: lexshort => shortlex 2021-07-23 17:21:58 -04:00
Slava Pestov
2d636955c6 RequirementMachine: Collapse RequirementMachine::Implementation down into RequirementMachine 2021-07-23 17:21:58 -04:00
Slava Pestov
27cf1cc4c2 RequirementMachine: Merge RequirementMachineImpl.h into RequirementMachine.h 2021-07-23 17:21:58 -04:00
Slava Pestov
212099be82 RequirementMachine: Move RequirementMachine class to swift::rewriting namespace 2021-07-23 17:21:58 -04:00
Slava Pestov
379359c08e RequirementMachine: Move RequirementMachine.h to lib/AST/RequirementMachine 2021-07-23 17:21:57 -04:00
Slava Pestov
d3db1b6753 RequirementMachine: EquivalenceClassMap => PropertyMap
EquivalenceClass is now PropertyBag.
2021-07-23 17:21:57 -04:00
Slava Pestov
0533b78ed8 RequirementMachine: Remove bogus copy-and-pasted comment 2021-07-23 15:58:50 -04:00
Slava Pestov
cfb1595ec5 RequirementMachine: Rename Atom => Symbol 2021-07-23 15:58:50 -04:00
Robert Widmann
1329f3cfbd [NFC] Lift getGenericEnvironment() into GenericSignature 2021-07-22 23:33:02 -07:00
Robert Widmann
d86551de67 Lift Requirement and Parameter Accessors up to GenericSignature
Start treating the null {Can}GenericSignature as a regular signature
with no requirements and no parameters. This not only makes for a much
safer abstraction, but allows us to simplify a lot of the clients of
GenericSignature that would previously have to check for null before
using the abstraction.
2021-07-22 23:27:05 -07:00
Slava Pestov
02db632488 RequirementMachine: Re-use a single global RewriteContext 2021-07-17 00:05:05 -04:00
Slava Pestov
b718663719 RequirementMachine: Tri-state enable flag, and move queries to GenericSignatureQueries.cpp
The -enable-requirement-machine and -disable-requirement-machine flags are now
replaced by a new flag -requirement-machine={on,off,verify}.
2021-07-17 00:05:05 -04:00
Slava Pestov
08edd56686 RequirementMachine: Drop protocols that the superclass conforms to when building an archetype 2021-07-17 00:05:05 -04:00
Slava Pestov
3e47d94b6c RequirementMachine: Implement GenericSignature::getLocalRequirements() query 2021-07-17 00:05:05 -04:00
Slava Pestov
dd6a569754 RequirementMachine: Fix use-after-free detected by ASAN
We're adding new rules to the Rules list while iterating over it, which
is not good.
2021-07-14 18:42:28 -04:00
Slava Pestov
4184ebd0a8 RequirementMachine: Split off RewriteContext.{h,cpp} from RewriteSystem.{h,cpp} 2021-07-14 00:16:06 -04:00
Slava Pestov
d7bb7e67d6 RequirementMachine: Implement GenericSignature::isCanonicalTypeInContext() query 2021-07-14 00:16:06 -04:00
Slava Pestov
499bff25bc RequirementMachine: Implement GenericSignature::getConformanceAccessPath() query
This is just a straight port of the existing code in the GSB, with minimal changes.

It could be made more efficient in the future by trafficking in Terms rather than
Types, avoiding some intermediate conversion and canonicalization steps.
2021-07-14 00:16:02 -04:00
Slava Pestov
b2ae546e17 RequirementMachine: Implement GenericSignature::lookupNestedType() query
This logic is mostly carried over from GenericSignatureBuilder::lookupNestedType().
2021-07-14 00:15:42 -04:00
Slava Pestov
422ae0ae5c RequirementMachine: Implement GenericSignature::getConcreteType() query 2021-07-14 00:15:42 -04:00
Slava Pestov
060f490983 RequirementMachine: Implement GenericSignature::getSuperclassBound() query 2021-07-14 00:15:42 -04:00
Slava Pestov
e4173add61 RequirementMachine: Fix superclass requirement vs concrete completion heuristic
If we have something like this:

    protocol P { associatedtype A : P }
    class C<U> : P { typealias A = C<U> }

    <T where T : P, T : C<U>>

Then we would add a rewrite T.A => T because both the concrete type
was equal to the type witness. But that is only valid if the original
requirement is a concrete type requirement, not a superclass requirement.
2021-07-14 00:15:42 -04:00
Slava Pestov
463b298aac RequirementMachine: New way to tie off type witness recursion
If two equivalence classes have the same concrete type and the
concrete type does not have any concrete substitutions, they are
essentially equivalent. Take advantage of this by introducing
same-type requirements to existing type witnesses where possible.
2021-07-14 00:15:42 -04:00
Slava Pestov
c32ec95c13 RequirementMachine: Enforce that rewrite rules preserve the term's 'domain'
Intuitively, the first token of a rewrite rule determines if the rule
applies to a protocol requirement signature 'Self', or a generic
parameter in the top-level generic signature. A rewrite rule can never
take a type starting with the protocol 'Self' to a type starting with a
generic parameter, or vice versa.

Enforce this by defining the notion of a 'domain', which is the set of
protocols to which the first atom in a term applies to. This set can be
empty (if we have a generic parameter atom), or it may contain more than
one element (if we have an associated type atom for a merged associated
type).
2021-07-14 00:14:29 -04:00
Slava Pestov
6077acad00 RequirementMachine: Handle null pointer return from getTypeWitness()
ProtocolConformance::getTypeWitness() can return a null Type in case of
circularity. Handle this by replacing it with an ErrorType to match
the behavior of the GSB.
2021-07-14 00:14:29 -04:00
Slava Pestov
05138121a5 RequirementMachine: Don't change concrete type to ErrorType in case of a conflict
This more closely matches the existing behavior of the GSB, where
conflicts are diagnosed but we don't drop all concrete type
requirements on that type parameter altogether.
2021-07-14 00:14:29 -04:00
Slava Pestov
5dca8c670b RequirementMachine: Perform nested type concretization for superclass requirements too
If an equivalence class has protocol conformance requirements together with a
superclass requirement, any protocols to which the superclass conforms might
have nested types that need to be concretized by introducing same-type
requierments between those nested types and the type witnesses in the
concrete conformances of the superclass.
2021-07-14 00:13:40 -04:00
Slava Pestov
ac5300cd6c RequirementMachine: Support DependentMemberTypes in remapConcreteSubstitutionSchema()
When concretizing nested types, we need to be able to normalize a concrete type
containing DependentMemberTypes to our 'concrete substitution schema' form where
all type parameter structural sub-components are actually just generic parameters.
2021-07-14 00:12:50 -04:00
Slava Pestov
5d61083aa1 RequirementMachine: Split off getRelativeTermForType() from concretizeNestedTypesFromConcreteParent()
The logic for handling the abstract type witness case correctly
handles DependentMemberTypes. We want to use this for concrete
type witnesses too, since they might contain type parameters as
structural sub-components.
2021-07-14 00:12:10 -04:00
Slava Pestov
e36b95d620 RequirementMachine: Split up list of associated types in ProtocolGraph
Store the protocol's direct associated types separately from the inherited
associated types, since in a couple of places we only need the direct
associated types.

Also, factor out a new ProtocolGraph::compute() method that does all the
steps in the right order.
2021-07-14 00:11:37 -04:00
Slava Pestov
00eca7ee97 RequirementMachine: Add reverse iterators over Term and MutableTerm
I thought I needed these for something but turns out that I don't,
but there's no harm in adding them since surely they will come up
eventually.
2021-07-14 00:06:17 -04:00
Slava Pestov
9a0c87b196 RequirementMachine: Implement GenericSignature::getCanonicalTypeInContext() query
We compute the canonical type by first simplifying the type term, and
then checking if it is a concrete type. If there's no concrete type,
we convert the simplified term back to an interface type and return
that; otherwise, we canonicalize any structural sub-components of
the concrete type that contain interface types, and so on.

Due to a quirk of how the existing declaration checker works, we also
need to handle "purely concrete" member types, eg if I have a
signature `<T where T == Foo>`, and we're asked to canonicalize the
type `T.[P:A]` where Foo : A.

This comes up because we can derive the signature `<T where T == Foo>`
from a generic signature like `<T where T : P>`; adding the
concrete requirement 'T == Foo' renders 'T : P' redundant. We then
want to take interface types written against the original signature
and canonicalize them with respect to the derived signature.

The problem is that `T.[P:A]` is not a valid term in the rewrite system
for `<T where T == Foo>`, since we do not have the requirement T : P.

A more principled solution would build a substitution map when
building a derived generic signature that adds new requirements;
interface types would first be substituted before being canonicalized
in the new signature.

For now, we handle this with a two-step process; we split a term up
into a longest valid prefix, which must resolve to a concrete type,
and the remaining suffix, which we use to perform a concrete
substitution using subst().
2021-07-09 00:04:36 -04:00
Slava Pestov
f0d59d5d4d RequirementMachine: Handle equivalence classes that both have a concrete type and protocol requirements
If a type parameter has a protocol conformance and a concrete type,
we want to map associated types of the conformance to their concrete
type witnesses.

This is implemented as a post-processing pass in the completion
procedure that runs after the equivalence class map has been built.

If we have an equivalence class T => { conforms_to: [ P ], concrete: Foo },
then for each associated type A of P, we generate a new rule:

    T.[P:A].[concrete: Foo.A] => T.[P:A]  (if Foo.A is concrete)
    T.[P:A] => T.(Foo.A)                  (if Foo.A is abstract)

If this process introduced any new rules, we check for any new
overlaps by re-running Knuth-Bendix completion; this may in turn
introduce new concrete associated type overlaps, and so on.

The overall completion procedure now alternates between Knuth-Bendix
and rebuilding the equivalence class map; the rewrite system is
complete when neither step is able to introduce any new rules.
2021-07-09 00:04:36 -04:00
Slava Pestov
35daf17dc4 RequirementMachine: Implement RewriteContext::getTypeForTerm()
Protocol atoms map to the 'Self' parameter; GenericParam and Name
atoms map in the obvious way.

An associated type atom `[P1&P1&...&Pn:A]` has one or more protocols
P0...Pn and an identifier 'A'.

We map it back to a AssociatedTypeDecl as follows:

- For each protocol Pn, look for associated types A in Pn itself,
  and all protocols that Pn refines.

- For each candidate associated type An in protocol Qn where
  Pn refines Qn, get the associated type anchor An' defined in
  protocol Qn', where Qn refines Qn'.

- Out of all the candidiate pairs (Qn', An'), pick the one where
  the protocol Qn' is the lowest element according to the linear
  order defined by TypeDecl::compare().

The associated type An' is then the canonical associated type
representative of the associated type atom `[P0&...&Pn:A]`.
2021-07-09 00:03:39 -04:00
Slava Pestov
2a1c141607 RequirementMachine: Implement EquivalenceClass::getConcreteType() 2021-07-08 23:31:53 -04:00
Slava Pestov
c35aa938de RequirementMachine: Add ProtocolGraph::isKnownProtocol() 2021-07-08 23:31:53 -04:00
Slava Pestov
658e1f2707 RequirementMachine: Verify simplified type terms in asserts builds 2021-07-08 23:31:53 -04:00
Slava Pestov
8ee58cd02c RequirementMachine: Fix assertion in mergeAssociatedTypes()
We used to assert that the merged protocol set was larger than or
equal to in size to both the left hand side and the right hand
side.

However, there is a counter-example:

    protocol P {}
    protocol Q {}
    protocol R : P, Q {}

    LHS = [P&Q:T], RHS = [R:T]

The merged atom is `[R:T]` which has fewer protocols than the
left hand side.
2021-07-08 23:31:53 -04:00
Slava Pestov
c97d32a654 RequirementMachine: getTermForType() produces resolved associated type atoms 2021-07-08 23:31:53 -04:00
Slava Pestov
e8eab411ba RequirementMachine: Store the current generic signature 2021-07-08 23:31:53 -04:00
Slava Pestov
56700983de RequirementMachine: Completion step and depth limits also apply to equivalence class map 2021-07-08 23:31:53 -04:00
Slava Pestov
05b3f79cec RequirementMachine: Implement GenericSignature::getRequiredProtocols() query 2021-06-30 01:34:20 -04:00
Slava Pestov
a37ad9fc89 RequirementMachine: Implement GenericSignature::areSameTypeParametersInContext() query 2021-06-30 01:34:20 -04:00
Slava Pestov
5d91b59e87 RequirementMachine: Implement GenericSignature::isConcreteType() query 2021-06-30 01:34:20 -04:00
Slava Pestov
62e0ad0f02 RequirementMachine: Add a dump() method 2021-06-30 01:34:20 -04:00
Slava Pestov
53d68144f8 RequirementMachine: Implement GenericSignature::getLayoutConstraint() query 2021-06-30 01:34:20 -04:00
Slava Pestov
8053b91c0e RequirementMachine: Implement GenericSignature::requiresProtocol() query 2021-06-30 01:34:20 -04:00
Slava Pestov
7df09f14ee RequirementMachine: Implement GenericSignature::requiresClass() query 2021-06-30 01:34:20 -04:00