mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
This rewrites the existing redundant requirements algorithm
to be simpler, and fix an incorrect behavior in the case where
we're building a protocol requirement signature.
Consider the following example:
protocol P {
associatedtype A : P
associatedtype B : P where A.B == B
}
The requirement B : P has two conformance paths here:
(B : P)
(A : P)(B : P)
The naive redundancy algorithm would conclude that (B : P) is
redundant because it can be derived as (A : P)(B : P). However,
if we drop (B : P), we lose the derived conformance path
as well, since it involves the same requirement (B : P).
The above example actually worked before this change, because we
handled this case in getMinimalConformanceSource() by dropping any
derived conformance paths that involve the requirement itself
appearing in the "middle" of the path.
However, this is insufficient because you can have a "cycle"
here with length more than 1. For example,
protocol P {
associatedtype A : P where A == B.C
associatedtype B : P where B == A.C
associatedtype C : P where C == A.B
}
The requirement A : P has two conformance paths here:
(A : P)
(B : P)(C : P)
Similarly, B : P has these two paths:
(B : P)
(A : P)(C : P)
And C : P has these two paths:
(C : P)
(A : P)(B : P)
Since each one of A : P, B : P and C : P has a derived conformance
path that does not involve itself, we would conclude that all three
were redundant. But this was wrong; while (B : P)(C : P) is a valid
derived path for A : P that allows us to drop A : P, once we commit to
dropping A : P, we can no longer use the other derived paths
(A : P)(C : P) for B : P, and (A : P)(B : P) for C : P, respectively,
because they involve A : P, which we dropped.
The problem is that we were losing information here. The explicit
requirement A : P can be derived as (B : P)(C : P), but we would
just say that it was implied by B : P alone.
For non-protocol generic signatures, just looking at the root is
still sufficient.
However, when building a requirement signature of a self-recursive
protocol, instead of looking at the root explicit requirement only,
we need to look at _all_ intermediate steps in the path that involve
the same protocol.
This is implemented in a new getBaseRequirements() method, which
generalizes the operation of getting the explicit requirement at
the root of a derived conformance path by returning a vector of
one or more explicit requirements that appear in the path.
Also the new algorithm computes redundancy online instead of building
a directed graph and then computing SCCs. This is possible by
recording newly-discovered redundant requirements immediately,
and then using the set of so-far-redundant requirements when
evaluating a path.
This commit introduces a small regression in an existing test case
involving a protocol with a 'derived via concrete' requirement.
Subsequent commits in this PR fix the regression and remove the
'derived via concrete' mechanism, since it is no longer necessary.
Fixes https://bugs.swift.org/browse/SR-14510 / rdar://problem/76883924.
85 lines
2.9 KiB
Swift
85 lines
2.9 KiB
Swift
// RUN: %target-typecheck-verify-swift
|
|
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s
|
|
|
|
protocol P1 {
|
|
associatedtype T
|
|
}
|
|
|
|
protocol P2 {
|
|
associatedtype T : P1
|
|
}
|
|
|
|
struct S1<A, B, C> : P2
|
|
where A : P1, C : P2, B == A.T.T, C.T == A.T {
|
|
typealias T = C.T
|
|
}
|
|
|
|
struct S2<D : P1> : P2 {
|
|
typealias T = D
|
|
}
|
|
|
|
// Make sure that we can resolve the nested type A.T
|
|
// in the same-type requirement 'A.T == B.T'.
|
|
//
|
|
// A is concrete, and S1<C, E, S2<D>>.T resolves to
|
|
// S2<D>.T, which is D. The typealias S1.T has a
|
|
// structural type 'C.T'; since C is concrete, we
|
|
// have to handle the case of an unresolved member
|
|
// type with a concrete base.
|
|
struct UnresolvedWithConcreteBase<A, B> {
|
|
// CHECK-LABEL: Generic signature: <A, B, C, D, E where A == S1<C, E, S2<D>>, B : P2, C : P1, D == B.T, E == D.T, B.T == C.T>
|
|
init<C, D, E>(_: C)
|
|
where A == S1<C, E, S2<D>>,
|
|
B : P2,
|
|
A.T == B.T,
|
|
C : P1,
|
|
D == C.T,
|
|
E == D.T { }
|
|
}
|
|
|
|
// Make sure that we drop the conformance requirement
|
|
// 'A : P2' and rebuild the generic signature with the
|
|
// correct same-type requirements.
|
|
//
|
|
// The original test case in the bug report (correctly)
|
|
// produces two warnings about redundant requirements.
|
|
struct OriginalExampleWithWarning<A, B> where A : P2, B : P2, A.T == B.T {
|
|
// CHECK-LABEL: Generic signature: <A, B, C, D, E where A == S1<C, E, S2<D>>, B : P2, C : P1, D == B.T, E == D.T, B.T == C.T>
|
|
init<C, D, E>(_: C)
|
|
where C : P1,
|
|
D : P1, // expected-warning {{redundant conformance constraint 'D' : 'P1'}}
|
|
// expected-note@-1 {{conformance constraint 'C.T' : 'P1' implied here}}
|
|
C.T : P1, // expected-warning {{redundant conformance constraint 'C.T' : 'P1'}}
|
|
A == S1<C, C.T.T, S2<C.T>>,
|
|
C.T == D,
|
|
E == D.T { }
|
|
}
|
|
|
|
// Same as above but without the warnings.
|
|
struct OriginalExampleWithoutWarning<A, B> where A : P2, B : P2, A.T == B.T {
|
|
// CHECK-LABEL: Generic signature: <A, B, C, D, E where A == S1<C, E, S2<D>>, B : P2, C : P1, D == B.T, E == D.T, B.T == C.T>
|
|
init<C, D, E>(_: C)
|
|
where C : P1,
|
|
A == S1<C, C.T.T, S2<C.T>>,
|
|
C.T == D,
|
|
E == D.T { }
|
|
}
|
|
|
|
// Same as above but without unnecessary generic parameters.
|
|
struct WithoutBogusGenericParametersWithWarning<A, B> where A : P2, B : P2, A.T == B.T {
|
|
// CHECK-LABEL: Generic signature: <A, B, C where A == S1<C, B.T.T, S2<B.T>>, B : P2, C : P1, B.T == C.T>
|
|
init<C>(_: C)
|
|
where C : P1,
|
|
C.T : P1, // expected-warning {{redundant conformance constraint 'C.T' : 'P1'}}
|
|
A == S1<C, C.T.T, S2<C.T>> {}
|
|
}
|
|
|
|
// Same as above but without unnecessary generic parameters
|
|
// or the warning.
|
|
struct WithoutBogusGenericParametersWithoutWarning<A, B> where A : P2, B : P2, A.T == B.T {
|
|
// CHECK-LABEL: Generic signature: <A, B, C where A == S1<C, B.T.T, S2<B.T>>, B : P2, C : P1, B.T == C.T>
|
|
init<C>(_: C)
|
|
where C : P1,
|
|
A == S1<C, C.T.T, S2<C.T>> {}
|
|
}
|