We had two similar loops that performed name lookup for nested types on a
potential archetype, so consolidate those into a single implementation on
the equivalence class itself.
RequirementSources, PotentialArchetypes, and EquivalenceClasses live as
long as the GenericSignatureBuilder. BumpPtrAllocate them all, introducing
a free list for equivalence classes because those can be transient.
Managing equivalence classes via the implicit structure of potential
archetypes is silly, and leads to weird patterns where we visit all
potential archetypes just to find the equivalence classes. Teach the
GSB implementation to manage equivalence classes in a linked list, so we
can easily iterate over them, and switch most uses of
`visitPotentialArchetypes()` over to that iteration.
Funnel all places where we create a generic signature builder to compute
the generic signature through a single entry point in the GSB
(`computeGenericSignature()`), and make `finalize` and `getGenericSignature`
private so no new uses crop up.
Tighten up the signature of `computeGenericSignature()` so it only works on
GSB rvalues, and ensure that all clients consider the GSB dead after that
point by clearing out the internal representation of the GSB.
We had two similar loops that performed name lookup for nested types on a
potential archetype, so consolidate those into a single implementation on
the equivalence class itself.
Once we compute a generic signature from a generic signature builder,
all queries involving that generic signature will go through a separate
(canonicalized) builder, and the original builder can no longer be used.
The canonicalization process then creates a new, effectively identical
generic signature builder. How silly.
Once we’ve computed the signature of a generic signature builder, “register”
it with the ASTContext, allowing us to move the existing generic signature
builder into place as the canonical generic signature builder. The builder
requires minimal patching but is otherwise fully usable.
Thanks to Slava Pestov for the idea!
Funnel all places where we create a generic signature builder to compute
the generic signature through a single entry point in the GSB
(`computeGenericSignature()`), and make `finalize` and `getGenericSignature`
private so no new uses crop up.
Tighten up the signature of `computeGenericSignature()` so it only works on
GSB rvalues, and ensure that all clients consider the GSB dead after that
point by clearing out the internal representation of the GSB.
We had two similar loops that performed name lookup for nested types on a
potential archetype, so consolidate those into a single implementation on
the equivalence class itself.
When computing the requirement signature of a protocol, eliminate requirement
sources that are self-derived by virtual of using a given requirement of
that protocol to prove that same constraint.
When there are to "structurally" equivalent potential archetypes in
different components of an equivalence class, as determined by their
nested-name structure, collapse the two components. This addresses the
remaining known issues due to the eager explosion of potential
archetypes in the generic signature builder.
Same-type constraints are rederived based on the potential archetypes
within an equivalence class and the same-type constraints that link
them. In some cases, there may be parts that connect those same-type
constraints that are stored in "delayed" requirements (for which one
or both end-points have not been realized in a potential archetype);
consider those as well for connectedness.
In a sense, this is an artifact of the generic signature builder's
approach of realizing potential archetypes too readily, and this can
likely be simplified in the future. For now, it's important for the
minimization of same-type constraints in generic signatures.
Nested-type-name-match constraints are odd because they are
effectively artifacts of the GenericSignatureBuilder's algorithm,
i.e., they occur when we have two PotentialArchetypes representing the
same type, and each of those PA's has a nested type based on the same
associated type. Because of this, nested-type-name-match constraints
have no useful requirement sources, so the normal means of detecting
self-derived constraints doesn't suffice, and we end up with
self-derived nested-type-name-match constraints eliminating the
constraints they depend on, causing spurious "redundant same-type
constraint" diagnostics and minimized generic signatures that drop
important requirements.
Handle nested-type-name-match constraints by first keeping them out of
the connected-components algorithm used to compute same-type
constraints within an equivalence class. Then, detect self-derived
nested-type-name-match constraints by determining whether any of the
ancestor potential archetypes are in the same equivalence class... and
redundant with the edge that makes the ancestor potential archetypes
equivalent. Remove such self-derived edges, and treat all other
nested-type-name-match edges as derived, providing a minimized
signature.
Fixes SR-5841, SR-5601, and SR-5610
When we have two nested types of a given potential archetype that have
the same name, introduce a (quietly) inferred constraint. This is
a future-proofing step for canonicalization, for a possible future
where we no longer require all of the nested types of a given name
to be equivalent, e.g., because we have a proper disambiguation
mechanism.
Queries through the GenericSignatureBuilder about a particular type
parameter only really need information about the equivalence class in which that type parameter resides. Introduce a new entry point
GenericSignatureBuilder::resolveEquivalenceClass() that only
For now, resolveEquivalanceClass() is a thin layer over
resolveArchetype().
Introduce support for writing a GraphViz graph describing the
same-type constraints within an equivalence class. This visualization
helps debugging the minimization algorithm.
LLVM's GraphWriter is actually kinda awful, so there is some hackery
here both to dodge an overzealous static assertion and to munge the
output into an undirected graph.
NFC except for debugging.
Reimplement isSelfDerivedSource() in terms of
getMinimalConformanceSource(), because they're fundamentally the same
thing. Also, replace the custom removeSelfDerived() implementation
within checkConformanceConstraints() by improving the common
checkConformanceConstraints() and teaching it to work on top of
getMinimalConformanceSource().
There is some hackery in here to make getMinimalConformanceSource()
behave like the now-removed isSelfDerivedSource(). It's all marked
with FIXMEs while I sort out the general form of "self-derived" for
same-type constraints. It's not as easy as I had hoped ;)
When we detect that a requirement source is self-derived, identify the
redundant subpath and remove it to produce a new, smaller requirement
source that computes the same result. We were doing this form the
limited case where the redundant subpath ended at the end of the
requirement source; generalize that notion.
Fixes SR-5601.
When a potential archetype refers to a concrete (non-associated) type
declaration, we bind to that concrete type. Add a new requirement
source kind for this case that is always derived, separating it from
the nested-type-name-match source.
One important aspect of this is that typealiases in protocols that
"override" an associated type an inherited protocol will generate the
same requirement signature as the equivalent protocol that uses a
same-type constraint, making the suppression of the "hey, this is
equivalent to a same-type constraint now!" warning an ABI-preserving
change.
With this, remove a now-unnecessary hack for nested-name-match
requirement sources.
Detect requirement sources that are internally self-derived for
(e.g.) handling of requirement sources for same-type constraints and
superclass constraints. This eliminates some incorrect warnings about
redundant same-type constraints involving recursive protocols, which
(more importantly) were a symptom of incorrect generic signature
minimization.
Fixes SR-5473.
More correctly fix SR-5485: we were retaining self-derived conformance
sources when we shouldn't, which led to spurious "redundant
conformance" diagnostics and (much worse) incorrect minimized generic
signatures. Now, when we detect a self-derived conformance source,
return the minimal source that will derive the same conformance... and
retain that one if it's new.