Skip to content

Commit 4dc7657

Browse files
committed
GSB: Canonicalize subject type in isValidDerivationPath()
Generally we say that a conformance requirement in a generic signature is redundant if there is some other way to derive this conformance by starting from another conformance requirement in the same signature, and possibly following one or more conformance requirements on nested types defined in protocols. The notion of a 'valid derivation path' comes into play when you have something like this: protocol P { associatedtype A } protocol Q { associatedtype B : P } <T where T : P, T.A : P, T.A.B == T> Here, we don't want to conclude that (T : P) can be derived from (T.A : P)(Self.B : P), because if we drop (T : P) from the signature, we end up with <T where T.A : P, T.A.B == T> Now in order to recover the witness table for T : P, we need to start from T.A : P, which requires the type metadata for T.A, which can only be recovered from the witness table for T : P, etc. We're stuck. What we want to do is say that T : P is not redundant, because we cannot derive it from T.A : P, because T.A depends on T : P. However, this check was also too strict. Consider this example: protocol P { associatedtype T where T == Self } protocol Q : P {} <T where T : P, T.T : Q> The naive algorithm would conclude that T : P is redundant because it can be derived as (T.T : Q)(Self.T == Self). However, the valid derivation path check would fail since T.T is derived from (T : P). The problem is that since T.T is equivalent to T via (Self.T == T), we would end up with this minimized signature: <T where T : P, T : Q> The derivation path check should canonicalize the type first. I'm still not 100% convinced this logic is correct, but at least, we have another test case and maybe it's _more_ correct now. Fixes part of rdar://problem/80503090.
1 parent 4434ee9 commit 4dc7657

File tree

2 files changed

+31
-10
lines changed

2 files changed

+31
-10
lines changed

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5701,6 +5701,11 @@ GenericSignatureBuilder::isValidRequirementDerivationPath(
57015701

57025702
auto otherSubjectType = otherReq.getSubjectType();
57035703

5704+
auto *equivClass = resolveEquivalenceClass(otherSubjectType,
5705+
ArchetypeResolutionKind::AlreadyKnown);
5706+
assert(equivClass &&
5707+
"Explicit requirement names an unknown equivalence class?");
5708+
57045709
// If our requirement is based on a path involving some other
57055710
// redundant requirement, see if we can derive the redundant
57065711
// requirement using requirements we haven't visited yet.
@@ -5720,11 +5725,6 @@ GenericSignatureBuilder::isValidRequirementDerivationPath(
57205725
if (!otherSource->isDerivedNonRootRequirement())
57215726
return None;
57225727

5723-
auto *equivClass = resolveEquivalenceClass(otherSubjectType,
5724-
ArchetypeResolutionKind::AlreadyKnown);
5725-
assert(equivClass &&
5726-
"Explicit requirement names an unknown equivalence class?");
5727-
57285728
switch (otherReq.getKind()) {
57295729
case RequirementKind::Conformance: {
57305730
auto *proto = otherReq.getRHS().get<ProtocolDecl *>();
@@ -5804,20 +5804,22 @@ GenericSignatureBuilder::isValidRequirementDerivationPath(
58045804
}
58055805
}
58065806

5807-
if (auto *depMemType = otherSubjectType->getAs<DependentMemberType>()) {
5807+
auto anchor = equivClass->getAnchor(*this, { });
5808+
5809+
if (auto *depMemType = anchor->getAs<DependentMemberType>()) {
58085810
// If 'req' is based on some other conformance requirement
58095811
// `T.[P.]A : Q', we want to make sure that we have a
58105812
// non-redundant derivation for 'T : P'.
58115813
auto baseType = depMemType->getBase();
58125814
auto *proto = depMemType->getAssocType()->getProtocol();
58135815

5814-
auto *equivClass = resolveEquivalenceClass(baseType,
5816+
auto *baseEquivClass = resolveEquivalenceClass(baseType,
58155817
ArchetypeResolutionKind::AlreadyKnown);
5816-
assert(equivClass &&
5818+
assert(baseEquivClass &&
58175819
"Explicit requirement names an unknown equivalence class?");
58185820

5819-
auto found = equivClass->conformsTo.find(proto);
5820-
assert(found != equivClass->conformsTo.end());
5821+
auto found = baseEquivClass->conformsTo.find(proto);
5822+
assert(found != baseEquivClass->conformsTo.end());
58215823

58225824
bool foundValidDerivation = false;
58235825
for (const auto &constraint : found->second) {

test/Generics/rdar80503090.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s
3+
4+
protocol P {
5+
associatedtype T where T == Self
6+
}
7+
8+
protocol Q : P {}
9+
10+
extension P {
11+
func missing() {}
12+
}
13+
14+
extension P where T : Q {
15+
// CHECK-LABEL: Generic signature: <Self where Self : Q>
16+
func test() {
17+
missing()
18+
}
19+
}

0 commit comments

Comments
 (0)