Skip to content

Commit 8157ccf

Browse files
authored
Teach SubstitutionMap::isIdentity about non-canonical generic params (#25767)
Do a weaker check here that only looks at the canonical generic params and guarantees that *those* substitute to themselves. There may be replacement types for other generic params too, to canonicalize them, but that's not a problem. This fixes a crash trying to mangle decls with opaque result types that have generic signatures that canonicalize away a generic parameter. rdar://problem/51775857
1 parent 7328503 commit 8157ccf

File tree

3 files changed

+99
-12
lines changed

3 files changed

+99
-12
lines changed

lib/AST/ASTMangler.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -973,9 +973,8 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) {
973973
auto opaqueType = cast<OpaqueTypeArchetypeType>(tybase);
974974
auto opaqueDecl = opaqueType->getDecl();
975975
if (opaqueDecl->getNamingDecl() == forDecl) {
976-
if (opaqueType->getSubstitutions().isIdentity()) {
977-
return appendOperator("Qr");
978-
}
976+
assert(opaqueType->getSubstitutions().isIdentity());
977+
return appendOperator("Qr");
979978
}
980979

981980
// Otherwise, try to substitute it.

lib/AST/SubstitutionMap.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -684,13 +684,25 @@ void SubstitutionMap::profile(llvm::FoldingSetNodeID &id) const {
684684
}
685685

686686
bool SubstitutionMap::isIdentity() const {
687-
for (unsigned i : indices(getReplacementTypes())) {
688-
auto replacement = dyn_cast<GenericTypeParamType>(
689-
getReplacementTypes()[i]->getCanonicalType(getGenericSignature()));
690-
if (!replacement)
691-
return false;
692-
if (getGenericSignature()->getGenericParamOrdinal(replacement) != i)
693-
return false;
694-
}
695-
return true;
687+
if (empty())
688+
return true;
689+
690+
GenericSignature *sig = getGenericSignature();
691+
unsigned countOfGenericParams = 0;
692+
bool hasNonIdentityReplacement = false;
693+
694+
sig->forEachParam([&](GenericTypeParamType *paramTy, bool isCanonical) {
695+
++countOfGenericParams;
696+
if (!isCanonical)
697+
return;
698+
699+
auto replacementTy = Type(paramTy).subst(*this);
700+
if (!paramTy->isEqual(replacementTy))
701+
hasNonIdentityReplacement = true;
702+
});
703+
704+
assert(countOfGenericParams == getReplacementTypes().size());
705+
(void)countOfGenericParams;
706+
707+
return !hasNonIdentityReplacement;
696708
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// This test makes sure we can generate USRs for a variety of declarations using
2+
// opaque result types, even in the presence of errors or unusual generic
3+
// signatures.
4+
5+
// RUN: %target-typecheck-verify-swift -disable-availability-checking
6+
// RUN: %target-swift-ide-test -print-usrs -source-filename %s | %FileCheck -strict-whitespace %s
7+
8+
// CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test0C21UnifyingGenericParams1xQrx_tq_Rszr0_lF
9+
func testUnifyingGenericParams<T, U>(x: T) -> some Collection where T == U {
10+
// expected-error@-1 {{same-type requirement makes generic parameters 'T' and 'U' equivalent}}
11+
return []
12+
}
13+
14+
// CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test0C22UnifyingGenericParams21xQrx_tSlRz7ElementQzRs_r0_lF
15+
func testUnifyingGenericParams2<T, U>(x: T) -> some Collection where T: Collection, U == T.Element {
16+
return []
17+
}
18+
19+
// CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test0C24ConcretizingGenericParam1xQrSi_tSiRszlF
20+
func testConcretizingGenericParam<T>(x: T) -> some Collection where T == Int {
21+
// expected-error@-1 {{same-type requirement makes generic parameter 'T' non-generic}}
22+
return []
23+
}
24+
25+
struct GenericContext<T> {
26+
// CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test14GenericContextV0c8UnifyingD6Params1xQrx_tqd__RszlF
27+
func testUnifyingGenericParams<U>(x: T) -> some Collection where T == U {
28+
// expected-error@-1 {{same-type requirement makes generic parameters 'U' and 'T' equivalent}}
29+
return []
30+
}
31+
32+
// CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test14GenericContextV0c8UnifyingD7Params21xQrx_tSlRz7ElementQzRsd__lF
33+
func testUnifyingGenericParams2<U>(x: T) -> some Collection where T: Collection, U == T.Element {
34+
return []
35+
}
36+
37+
// CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test14GenericContextVyQrxcqd__Rszluip
38+
subscript<U>(x: T) -> some Collection where T == U {
39+
// expected-error@-1 {{same-type requirement makes generic parameters 'U' and 'T' equivalent}}
40+
// CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test14GenericContextVyQrxcqd__Rszluig
41+
get {
42+
return []
43+
}
44+
}
45+
}
46+
47+
extension GenericContext where T == Int {
48+
// CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test14GenericContextVAASiRszlE0c12ConcretizingD5Param1xQrSi_tF
49+
func testConcretizingGenericParam(x: T) -> some Collection {
50+
return []
51+
}
52+
}
53+
54+
struct TooGenericTooContext<T, U> {
55+
}
56+
57+
extension TooGenericTooContext where T == U {
58+
// CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test010TooGenericD7ContextVAAq_RszrlE0c8UnifyingE6Params1xQrx_tF
59+
func testUnifyingGenericParams(x: T) -> some Collection {
60+
return []
61+
}
62+
}
63+
64+
extension TooGenericTooContext where T: Collection, U == T.Element {
65+
// CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test010TooGenericD7ContextVAASlRz7ElementQzRs_rlE0c8UnifyingE7Params21xQrx_tF
66+
func testUnifyingGenericParams2(x: T) -> some Collection {
67+
return []
68+
}
69+
}
70+
extension TooGenericTooContext where T == Int {
71+
// CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test010TooGenericD7ContextVAASiRszrlE0c12ConcretizingE5Param1xQrSi_tF
72+
func testConcretizingGenericParam(x: T) -> some Collection {
73+
return []
74+
}
75+
}
76+

0 commit comments

Comments
 (0)