Commit Graph

725 Commits

Author SHA1 Message Date
Slava Pestov
977d3a77cf GSB: Narrow down the condition under which we rebuild a generic signature 2021-04-08 01:25:41 -04:00
Slava Pestov
bcee54b936 GSB: The combination of a superclass and conformance requirement might force a type to be concrete
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.
2021-04-03 23:04:39 -04:00
Slava Pestov
7e17aaf094 GSB: Remove one of the two overloads of checkConstraintList() 2021-04-03 22:33:14 -04:00
Slava Pestov
61f796da5a GSB: Remove unused parameter from lookupConformance() 2021-04-03 22:33:14 -04:00
Slava Pestov
bc1717b7b3 GSB: Factor out updateLayout() from addLayoutRequirementDirect() 2021-04-03 22:33:14 -04:00
Slava Pestov
cb25f2ba47 GSB: Diagnose conflicts between concrete-type and AnyObject requirements 2021-04-03 22:33:14 -04:00
Slava Pestov
f190e51f8d GSB: Diagnose redundant superclass requirements using the redundant requirement graph 2021-04-03 22:33:14 -04:00
Slava Pestov
4ae755e6fb GSB: Fix handling of layout requirements in stripBoundDependentMemberTypes()
There was a silly typo here. Also add some asserts to the
Requirement constructor to catch this if it happens again.

Fixes rdar://problem/75691385.
2021-03-28 00:10:06 -04:00
Slava Pestov
1d9b202719 GSB: Fix inconsistent 'redundant conformance' diagnostic 2021-03-27 00:35:19 -04:00
Slava Pestov
299ff02684 GSB: Diagnose redundant layout requirements using the redundant requirement graph 2021-03-27 00:35:19 -04:00
Slava Pestov
1617727e12 GSB: Refactor computeRedundantRequirements() in preparation for redoing conflict checking 2021-03-27 00:35:19 -04:00
Slava Pestov
524e588bf3 GSB: Skip same-type requirement minimization if there are no same-type requirements
The logic here is pretty expensive, and there's no reason to
do it at all if we aren't going to end up with any same-type
requirements.
2021-03-26 01:29:42 -04:00
Slava Pestov
a7f2e5c50b GSB: Use explicit requirement list in enumerateRequirements() 2021-03-26 01:29:42 -04:00
Slava Pestov
74b29caed3 GSB: Fix bug with concrete same-type requirements to self-conforming protocols
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.
2021-03-26 01:29:42 -04:00
Slava Pestov
389a082985 GSB: Assert that concrete types should not have any other requirements
This sort of thing is not valid and should never happen:

    <T where T : P, T == Int>
2021-03-26 01:29:42 -04:00
Slava Pestov
7442e3ad04 GSB: Use a SmallSetVector to store the explicit requirements
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.
2021-03-26 01:29:42 -04:00
Slava Pestov
99d49f0ccb GSB: When rebuilding a signature, use the as-written requirements instead of the minimized ones
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.
2021-03-25 17:35:52 -04:00
Slava Pestov
7d3a8911b1 GSB: Don't canonicalize concrete type or superclass of equivalence class 2021-03-25 17:33:31 -04:00
Slava Pestov
319bc071bd GSB: Use compareRequirements() in enumerateRequirements() 2021-03-25 17:33:31 -04:00
Slava Pestov
1fe4092b10 GSB: Extract out compareRequirements() from checkGenericSignature()
I want to use the same predicate for sorting requirements as what
we use to assert that the signature was valid.
2021-03-24 18:52:48 -04:00
Slava Pestov
ae133bc39b GSB: Use explicit requirement list when diagnosing redundant requirements 2021-03-24 18:45:04 -04:00
Slava Pestov
1bc4a4f7e2 GSB: Refactor enumerateRequirements() a bit
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.
2021-03-24 18:45:04 -04:00
Slava Pestov
b921c8c7a7 GSB: Record all explicitly-written requirements as they are added
This will allow us to rebuild the generic signature using the
as-written requirements, instead of the previously-minimized
signature.
2021-03-24 18:45:04 -04:00
Slava Pestov
bed33d82af GSB: Don't compute anchors in resolveDependentMemberTypes()
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.
2021-03-24 18:45:04 -04:00
Slava Pestov
87cf15a85d GSB: Replace some resolveDependentMemberTypes() calls with getCanonicalTypeInContext()
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.
2021-03-24 18:45:04 -04:00
Slava Pestov
568320c1d3 GSB: Move guts of getCanonicalTypeInContext() from GenericSignature to the GSB 2021-03-24 18:45:04 -04:00
Slava Pestov
4921e061a4 GSB: Fix over-eager caching in EquivalenceClass::lookupNestedType()
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.
2021-03-24 12:56:17 -04:00
Slava Pestov
412ceefb40 GSB: Handle unresolved member types with a concrete base in substituteConcreteType()
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.
2021-03-24 12:54:36 -04:00
Slava Pestov
c9037a7bac Merge pull request #36483 from slavapestov/use-redundant-requirement-graph-for-diagnostics-part-1
GSB: Use redundant requirements graph to diagnose redundant conformance requirements
2021-03-18 10:20:23 -04:00
Slava Pestov
bb5df9dd4f GSB: Use redundant requirements graph to diagnose redundant conformance requirements
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.
2021-03-18 00:16:28 -04:00
Slava Pestov
17e3ee7ccf GSB: Don't emit diagnostics with <<error type>> in them 2021-03-17 23:07:11 -04:00
Slava Pestov
afa08f01a1 GSB: Formalize the old hack where we rebuild a signature that had redundant conformance requirements
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.
2021-03-17 17:25:41 -04:00
Slava Pestov
60c17557e1 GSB: Generalize redundant requirement algorithm to record which requirement implied a redundant requirement
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().
2021-03-17 16:20:40 -04:00
Slava Pestov
adc393890d GSB: Remove unused parameter to FloatingRequirementSource::isRecursive() 2021-03-17 16:20:40 -04:00
Slava Pestov
8e4598e622 GSB: Narrow scope of conditional requirement inference
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.
2021-03-16 23:29:13 -04:00
Slava Pestov
937bfd9bbb Merge pull request #36416 from slavapestov/gsb-infinite-recursion
GSB: Fix runaway recursion through EquivalenceClass::lookupNestedType()
2021-03-12 20:38:21 -05:00
Slava Pestov
2bd850c56f GSB: Fix runaway recursion through EquivalenceClass::lookupNestedType()
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.
2021-03-12 01:24:47 -05:00
Slava Pestov
91ebe4485c GSB: Move signature verification from CanGenericSignature::getCanonical() to computeGenericSignature()
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.
2021-03-11 21:07:33 -05:00
Slava Pestov
124b8fd546 GSB: Rename updateNestedTypeForConformance() to getOrCreateNestedType() and simplify it 2021-03-05 21:32:27 -05:00
Slava Pestov
0be55c130d GSB: Use redundant requirement graph in enumerateRequirements() 2021-03-01 17:50:26 -05:00
Slava Pestov
72bd35a344 GSB: New algorithm for identifying redundant requirements
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.
2021-03-01 17:19:22 -05:00
Slava Pestov
ce8549b31b GSB: Record the original type for self-derived conformances
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.
2021-02-28 23:41:56 -05:00
Slava Pestov
3a93a178e2 GSB: Remove RequirementSource::ConcreteTypeBinding 2021-02-26 22:20:32 -05:00
Slava Pestov
00ed470844 GSB: Replace RequirementSource::Derived with ::Layout
'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
2021-02-26 14:55:40 -05:00
Slava Pestov
f8b293e8c9 GSB: RequirementRHS can now store a ProtocolDecl * 2021-02-26 14:53:52 -05:00
Slava Pestov
24de1913ac GSB: Merge collectRequirements() with enumerateRequirements() 2021-02-25 17:19:23 -05:00
Slava Pestov
e0c7627598 GSB: getBestConstraintSource() never returns nullptr 2021-02-25 17:19:23 -05:00
Slava Pestov
b9f5267ac1 GSB: Stop printing enumerated requirements in dump()
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.
2021-02-25 17:19:23 -05:00
Slava Pestov
34d3236316 GSB: Avoid re-resolving FloatingRequirementSources 2021-02-24 16:33:38 -05:00
Slava Pestov
39397d76a1 GSB: Remove HadAnyRedundantConstraints
Redundant requirements no longer pose a problem for the new
getConformanceAccessPath() implementation.
2021-02-23 16:26:04 -05:00