A protocol can constrain an associated type to Self:
protocol P {
associatedtype A : Q where A.B == Self
}
protocol Q {
associatedtype B
}
And a class might conform to this protocol:
class C : P {
typealias A = D
}
class D : Q {
typealias B = C
}
The generic signature <Self where Self : P, Self : C> is built during
conformance checking. Since Self : C, we must have that Self.A == D;
since D.B == C, the requierement 'A.B == Self' in protocol P implies
that 'Self == C'. So the correct minimized signature here is
<Self where Self == C>.
This wasn't handled properly before, because of assumptions in
removeSelfDerived() and a couple of other places.
Fixes rdar://71677712, rdar://76155506, https://bugs.swift.org/browse/SR-10033,
https://bugs.swift.org/browse/SR-13884.
If a type parameter both conformed to Error and was required to equal
Error, we didn't record a concrete source for the Error conformance.
This still produced the right minimized signature because the code in
enumerateRequirements() happens to skip all other requirements if an
equivalence class was concrete, but we shouldn't rely on that.
The bug also meant that we didn't diagnose the redundant conformance
requirement.
We can literally have duplicate requirements (same source and RHS)
when abstract signatures are built from scratch. They're not
flagged as "redundant" since there's no way to distinguish them
in the requirement graph, so just filter them out entirely.
We rebuild a signature after dropping redundant conformance requirements,
since conformance requirements change the canonical type and conformance
access path computation.
When doing this, instead of using the canonical requirements from the
signature, use the original as-written requirements.
This fixes some weirdness around concrete same-type requirements.
There's a philosophical benefit here too -- since we rebuild the
signature without ever having built the old signature, we never create
an invalid GenericSignature in the ASTContext.
Fixes rdar://problem/75690903.
Extract out createRequirement(), since I'm going to be using it soon.
Also, merge the two loops together to do a single walk over all the
equivalence classes, instead of pointlessly building a list of
SameTypeComponentRefs first.
When a concrete type contains unresolved DependentMemberTypes, we
don't need to replace them with their anchor, just the resolved
representative from the PotentialArchetype.
This means that the only time we compute anchors are when
computing canonical types (which is only done after a GSB has
been finalized) and when adding same-type requirements.
This should eventually enable building the same-type rewrite
system in one shot.
For now this has no effect since resolveDependentMemberTypes() maps
type parameters to anchors, but I'm going to change it to not do
that.
Change all existing callers of resolveDependentMemberTypes() except
for the one in maybeResolveEquivalenceClass() to use
getCanonicalTypeInContext() instead.
We would invalidate the cache if the superclass changed, but
not the concrete type. Let's do the same with the concrete
type.
This fixes one small part of rdar://problem/75656022.
Type::subst() does not support DependentMemberTypes that only have
an identifier and not an associated type. Instead, when we hit one
of these, replace the generic parameter at the root with its
replacement type only, then rely on maybeResolveEquivalenceClass()
getting called again, which will perform the name lookup to resolve
the member type.
This almost completely guts checkConformanceConstraints(), and
removes a usage of checkConstraintList().
Yet another one of those refactorings that leaves the GSB in an
intermediate state, with some new logic that's cleaner, while
leaving behind old code for other use cases that haven't been
refactored yet.
When constructing a generic signature, any redundant explicit requirements
are dropped from the final signature.
We would assume this operation is idempotent, that is, building a new
GenericSignatureBuilder from the resulting minimized signature produces
an equivalent GenericSignatureBuilder to the original one.
Unfortunately, this is not true in the case of conformance requirements.
Namely, if a conformance requirement is made redundant by a superclass
or concrete same-type requirement, then dropping the conformance
requirement changes the canonical type computation.
For example, consider the following:
public protocol P {
associatedtype Element
}
public class C<O: P>: P {
public typealias Element = O.Element
}
public func toe<T, O, E>(_: T, _: O, _: E, _: T.Element)
where T : P, O : P, O.Element == T.Element, T : C<E> {}
In the generic signature of toe(), the superclass requirement 'T : C<E>'
implies the conformance requirement 'T : P' because C conforms to P.
However, the presence of the conformance requirement makes it so that
T.Element is the canonical representative, so previously this signature
was minimized down to:
<T : C<E>, O : P, T.Element == O.Element>
If we build the signature again from the above requirements, then we
see that T.Element is no longer the canonical representative; instead,
T.Element canonicalizes as E.Element.
For this reason, we must rebuild the signature to get the correct
canonical type computation.
I realized that this is not an artifact of incorrect design in the
current GSB; my new rewrite system formalism would produce the same
result. Rather, it is a subtle consequence of the specification of our
minimization algorithm, and therefore it must be formalized in this
manner.
We used to sort-of do this with the HadAnyRedundantRequirements hack,
but it was both overly broad (we only need to rebuild if a conformance
requirement was implied by a superclass or concrete same-type
requirement) and not sufficient (when rebuilding, we need to strip any
bound associated types from our requirements to ensure the canonical
type anchors are re-computed).
Fixes rdar://problem/65263302, rdar://problem/75010156,
rdar://problem/75171977.
Recall the meaning of an explicit requirement here:
- If the requirement is part of a root SCC, it is redundant
unless it is the 'best' requirement from that root SCC.
- If the requirement is part of a non-root SCC, it is always
redundant.
Instead of computing the set of redundant requirements, we
build a mapping.
The value in the mapping stores the set of root requirements
that imply the given redundant requirement. This mapping is
computed by traversing the graph from each root, recording
which components can be reached from each root.
For now, I'm using this to fix rdar://problem/65263302.
After fixing that bug, this will also allow us to radically
simplify the various callers of checkConstraintList().
If we have a conformance requirement T : P, and a concrete type
requirement T == G<...>, and G _conditionally_ conforms to P,
we would infer the conditional requirements of G needed to
satisfy the conformance.
However, if the conformance requirement T : P was not explicit,
this would mean in practice that we would need to infer an
infinite number of conditional requirements, because there
might be an infinite number of types T for which T : P.
Previously we would infer these up to some limit, based on
how many levels of nested types the GSB had expanded.
Since this is untenable, let's instead change the rules so
that conditional requirement inference is only performed
when the concretizing requirement was explicit.
We shouldn't generate NestedTypeNameMatch same-type constraints
between associated types we haven't realized yet.
Otherwise, since maybeResolveEquivalenceClass() can call
lookupNestedType() before looking up a PotentialArchetype, it
is possible that maybeResolveEquivalenceClass() will return
the newly-realized type even when resolutionKind is AlreadyKnown.
This can cause an infinite recursion in expandConformanceRequirement().
However, we don't really have to do this here at all, because if
a PotentialArchetype is registered with the same name later, we
will introduce the same-type constraint in addedNestedType().
It suffices for lookupNestedType() to return the best associated
type anchor and ignore the rest.
Fixes https://bugs.swift.org/browse/SR-14289 / rdar://problem/74876047.
Doing this when computing a canonical signature didn't really
make sense because canonical signatures are not canonicalized
any more strongly _with respect to the builder_; they just
canonicalize their requirement types.
Instead, let's do these checks after creating the signature in
computeGenericSignature().
The old behavior had another undesirable property; since the
canonicalization was done by registerGenericSignatureBuilder(),
we would always build a new GSB from scratch for every
signature we compute.
The new location also means we do these checks for protocol
requirement signatures as well. This flags an existing fixed
crasher where we still emit bogus same-type requirements in
the requirement signature, so I moved this test back into
an unfixed state.
Generic signature minimization needs to diagnose and remove any
redundant requirements, that is, requirements that can be proven
from some subset of the remaining requirements.
For each requirement on an equivalence class, we record a set of
RequirementSources; a RequirementSource is a "proof" that the
equivalence class satisfies the requirement.
A RequirementSource is either "explicit" -- meaning it
corresponds to a generic requirement written by the user -- or it
is "derived", meaning it can be proven from some other explicit
requirement.
The most naive formulation of the minimization problem is that
we say that an explicit requirement is redundant if there is a
derived source for the same requirement. However, this is not
sufficient, for example:
protocol P {
associatedtype A : P where A.A == Self
}
In the signature <T where T : P>, the explicit requirement
T : P also has a derived source T.A.A : P. However, this source
is "self-derived", meaning that in order to obtain the
witness table for T.A.A : P, we first have to obtain the witness
table for T.A.
The GenericSignatureBuilder handled this kind of 'self-derived'
requirement correctly, by removing it from the list of sources.
This was implemented in the removeSelfDerived() function.
After removeSelfDerived() was called, any remaining derived
requirement sources were assumed to obsolete any explicit
source for the same requirement.
However, even this was not sufficient -- namely, it only handled
the case where a explicit requirement would imply a derived
source for itself, and not a cycle involving multiple explicit
sources that would imply each other.
For example, the following generic signature would be misdiagnosed
with *both* conformance requirements as redundant, resulting in
an invalid generic signature:
protocol P {
associatedtype T : P
}
func f<T : P, U : P>(_: T, _: U) where T.X == U, U.X == T {}
In the above example, T : P has an explicit requirement source,
as well as a derived source (U : P)(U.X : P). Similarly, U : P
has an explicit requirement source, as well as a derived source
(T : P)(T.X : P). Since neither of the derived sources were
"self-derived" according to our definition, we would diagnose
*both* explicit sources as redundant. But of course, after
dropping them, we are left with the following generic signature:
func f<T, U>(_: T, _: U) where T.X == U, U.X == T {}
This is no longer valid -- since neither T nor U have a conformance
requirement, the nested types T.X and U.X referenced from our
same-type requirements are no longer valid.
The new algorithm abandons the "self-derived" concept. Instead,
we build a directed graph where the vertices are explicit
requirements, and the edges are implications where one explicit
requirement implies another. In the above example, each of the
explicit conformance requirements implies the other. This means
a correct minimization must pick exactly one of the two -- not
zero, and not both.
The set of minimized requirements is formally defined as the
minimum set of requirements whose transitive closure is the
entire graph.
We compute this set by first building the graph of strongly
connected components using Tarjan's algorithm. The graph of SCCs
is a directed acyclic graph, which means we can compute the root
set of the DAG. Finally, we pick a suitable representative
requirement from each SCC in the root set, using the lexshort
order on subject types.
This commit implements the new algorithm, runs it on each generic
signature and asserts some properties about the results, but
doesn't actually use the algorithm for computing the minimized
signature or diagnosing redundancies; that will come in the next
commit.
RequirementSources with Superclass and Concrete kind would
reference a protocol conformance. However in the case where
the concrete type was an existential conforming to itself,
the SelfProtocolConformance does not store the original
type. Since self-conforming existentials don't have any
nested types, we don't really need to store this conformance
at all.
Instead of storing a protocol conformance in the case where
the original type is an existential, just the original type
itself. This allows us to recover the requirement from the
RequirementSource, which is important for the new
implementation of computing redundant requirements.
'Derived' was not a great name, since we already use the term
'derived requirement source' to mean something else.
A Derived source was only added in one place, when recording
a superclass constraint -- the idea is that this source
supercedes any explicit layout constraint, eg
class SomeClass {}
func foo<T>(_: T) where T : SomeClass, T : AnyObject {}
Here we have two sources for the 'T : AnyObject' layout constraint:
Explicit: T
Explicit: T -> Derived
Note that the 'Derived' requirement source does not store
a 'proof' -- we can't figure out _how_ we determined that
the explicit 'T : AnyObject' constraint is redundant here.
In the case where a superclass requirement makes a protocol
conformance redundant, we do have a 'proof', because the
'Superclass' requirement source stores a conformance:
class SomeClass : SomeProto {}
func foo<T>(_: T) where T : SomeClass, T : SomeProto {}
Explicit: T
Explicit: T -> Superclass: [SomeClass : P]
From looking at the second requirement source, we can
determine that the requirement was imposed by the explicit
constraint 'T : SomeClass'.
For the 'Layout' requirement source, there's not really a
"conformance", so we can just store the superclass type:
Explicit: T
Explicit: T -> Layout: SomeClass
The call to enumerateRequirements() here actually makes debugging
more difficult, since it has a lot of side effects, for example
calling maybeResolveEquivalenceClass() and removeSelfDerived().
Also, this is the only usage of enumerateRequirements() other than
collectRequirements(), which allows the two to be merged together
and simplified.