Skip to content

Commit 34e6ed9

Browse files
committed
NCGenerics: fix runtime demangling strategy
We can obtain metadata on older runtimes for a concrete type with a Copyable generic parameter via demangling. But if the generic argument is noncopyable, we should use the metadata response strategy instead. resolves rdar://131337585
1 parent 5bddd98 commit 34e6ed9

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

lib/IRGen/GenReflection.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,52 @@ class PrintMetadataSource
175175
}
176176
};
177177

178+
/// Any nominal type that has an inverse requirement in its generic signature
179+
/// uses NoncopyableGenerics. Since inverses are mangled into symbols,
180+
/// a Swift 6.0+ runtime is generally needed to demangle them.
181+
///
182+
/// But, if the type is a BoundGenericType, where the generic arguments are all
183+
/// concrete types that DO conform to ALL invertible protocols (Copyable, etc),
184+
/// then older runtimes can obtain the metadata via demangling using the
185+
/// \c swift_instantiateConcreteTypeFromMangledName runtime call.
186+
///
187+
/// \returns true if the Swift 6.0+ runtime is needed to demangle this type.
188+
static bool demanglingNeedsNeedsNoncopyableGenericsAwareness(
189+
NominalOrBoundGenericNominalType *nominalTy) {
190+
auto *nom = nominalTy->getDecl();
191+
if (auto sig = nom->getGenericSignature()) {
192+
SmallVector<InverseRequirement, 2> inverses;
193+
SmallVector<Requirement, 2> reqs;
194+
sig->getRequirementsWithInverses(reqs, inverses);
195+
if (!inverses.empty()) { // then it's using NoncopyableGenerics.
196+
if (!isa<BoundGenericType>(nominalTy))
197+
return true;
198+
199+
// If all of the generic arguments in this BGT are Copyable/Escapable,
200+
// then it should not require the newer runtime.
201+
auto boundTy = cast<BoundGenericType>(nominalTy);
202+
for (auto arg : boundTy->getGenericArgs()) {
203+
// If the BGT is not fully concrete, assume it requires newer runtime.
204+
if (arg->hasTypeParameter() || arg->hasUnboundGenericType())
205+
return true;
206+
207+
for (auto ip : InvertibleProtocolSet::allKnown()) {
208+
switch (ip) {
209+
case InvertibleProtocolKind::Copyable:
210+
if (arg->isNoncopyable())
211+
return true;
212+
break;
213+
case InvertibleProtocolKind::Escapable:
214+
if (!arg->isEscapable())
215+
return true;
216+
}
217+
}
218+
}
219+
}
220+
}
221+
return false; // any runtime is fine
222+
}
223+
178224
std::optional<llvm::VersionTuple>
179225
getRuntimeVersionThatSupportsDemanglingType(CanType type) {
180226
enum VersionRequirement {
@@ -226,6 +272,12 @@ getRuntimeVersionThatSupportsDemanglingType(CanType type) {
226272
// involving them.
227273
}
228274

275+
276+
if (auto nominalTy = dyn_cast<NominalOrBoundGenericNominalType>(t)) {
277+
if (demanglingNeedsNeedsNoncopyableGenericsAwareness(nominalTy))
278+
return addRequirement(Swift_6_0);
279+
}
280+
229281
return false;
230282
});
231283

@@ -358,6 +410,7 @@ getTypeRefByFunction(IRGenModule &IGM,
358410
Address(bindingsBufPtr, IGM.Int8Ty, IGM.getPointerAlignment()),
359411
MetadataState::Complete, subs);
360412

413+
substT = substT.getReferenceStorageReferent();
361414
auto ret = IGF.emitTypeMetadataRef(substT);
362415
IGF.Builder.CreateRet(ret);
363416
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %swift-frontend %s -swift-version 6 -module-name main -emit-ir -o %t/new.ir
3+
// RUN: %FileCheck %s --check-prefix=NEW < %t/new.ir
4+
// RUN: %target-swift-frontend %s -target %target-cpu-apple-macosx11 -module-name main -emit-ir -o %t/old.ir
5+
// RUN: %FileCheck %s --check-prefix=OLD < %t/old.ir
6+
7+
// Check that we add extra type metadata accessors for types with generic
8+
// parameters that have an inverse. These are used instead of using demangling
9+
// cache variables since old runtimes cannot synthesize type metadata based on
10+
// the new mangling.
11+
12+
// RUN: %target-build-swift -target %target-cpu-apple-macosx11 %s -o %t/test_mangling
13+
// RUN: %target-run %t/test_mangling | %FileCheck %s
14+
15+
// REQUIRES: OS=macosx
16+
// REQUIRES: executable_test
17+
18+
19+
// This type's generic parameter is noncopyable, so older runtimes can't
20+
// demangle the type's name to build the metadata.
21+
struct Foo<T: ~Copyable>: ~Copyable {
22+
mutating func bar(_ i: Int) { print("Foo.bar(\(i))") }
23+
}
24+
25+
func test() {
26+
var foo = Foo<Int>()
27+
foo.bar(1)
28+
}
29+
test()
30+
// CHECK: Foo.bar(1)
31+
32+
// NEW: define hidden swiftcc void @"$s4main4testyyF"()
33+
// NEW-NOT: %swift.metadata_response
34+
// NEW: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main3FooVySiGMD")
35+
// NEW-NOT: %swift.metadata_response
36+
// NEW: }
37+
38+
// OLD: define hidden swiftcc void @"$s4main4testyyF"()
39+
// OLD-NOT: %swift.metadata_response
40+
// OLD: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main3FooVySiGMD")
41+
// OLD-NOT: %swift.metadata_response
42+
// OLD: }
43+
44+
struct NC: ~Copyable {}
45+
46+
// NEW: define hidden swiftcc void @"$s4main10testWithNCyyF"()
47+
// NEW-NOT: %swift.metadata_response
48+
// NEW: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main3FooVyAA2NCVGMD")
49+
// NEW-NOT: %swift.metadata_response
50+
// NEW: }
51+
52+
// OLD: define hidden swiftcc void @"$s4main10testWithNCyyF"()
53+
// OLD-NOT: __swift_instantiateConcreteTypeFromMangledName
54+
// OLD: call swiftcc %swift.metadata_response @"$s4main3FooVyAA2NCVGMa"(i64 0)
55+
// OLD-NOT: __swift_instantiateConcreteTypeFromMangledName
56+
// OLD: }
57+
func testWithNC() {
58+
var foo = Foo<NC>()
59+
foo.bar(2)
60+
}
61+
testWithNC()
62+
// CHECK: Foo.bar(2)
63+
64+
65+
// NEW: define hidden swiftcc void @"$s4main17testWithNCGenericyyxnRi_zlF"
66+
// NEW-NOT: __swift_instantiateConcreteTypeFromMangledName
67+
// NEW: call swiftcc %swift.metadata_response @"$s4main3FooVMa"
68+
// NEW-NOT: __swift_instantiateConcreteTypeFromMangledName
69+
// NEW: }
70+
71+
// OLD: define hidden swiftcc void @"$s4main17testWithNCGenericyyxnRi_zlF"
72+
// OLD-NOT: __swift_instantiateConcreteTypeFromMangledName
73+
// OLD: call swiftcc %swift.metadata_response @"$s4main3FooVMa"
74+
// OLD-NOT: __swift_instantiateConcreteTypeFromMangledName
75+
// OLD: }
76+
func testWithNCGeneric<T: ~Copyable>(_ t: consuming T) {
77+
var foo = Foo<T>()
78+
foo.bar(3)
79+
}
80+
testWithNCGeneric(Foo<NC>())
81+
// CHECK: Foo.bar(3)
82+
83+
84+
// This type does not need a Swift 6.0 runtime, despite being noncopyable,
85+
// because it doesn't have a noncopyable generic parameter.
86+
struct JustNoncopyable<T>: ~Copyable {
87+
mutating func bar() { print("JustNoncopyable.bar") }
88+
}
89+
90+
func testNonGeneric() {
91+
var ng = JustNoncopyable<Int>()
92+
ng.bar()
93+
}
94+
testNonGeneric()
95+
// CHECK: JustNoncopyable.bar
96+
97+
// NEW: define hidden swiftcc void @"$s4main14testNonGenericyyF"()
98+
// NEW-NOT: %swift.metadata_response
99+
// NEW: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main15JustNoncopyableVySiGMD")
100+
// NEW-NOT: %swift.metadata_response
101+
// NEW: }
102+
103+
// OLD: define hidden swiftcc void @"$s4main14testNonGenericyyF"()
104+
// OLD-NOT: %swift.metadata_response
105+
// OLD: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main15JustNoncopyableVySiGMD")
106+
// OLD-NOT: %swift.metadata_response
107+
// OLD: }

0 commit comments

Comments
 (0)