Skip to content

Commit 801fd85

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 (cherry picked from commit 34e6ed9)
1 parent c43fa37 commit 801fd85

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
@@ -174,6 +174,52 @@ class PrintMetadataSource
174174
}
175175
};
176176

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

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

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

412+
substT = substT.getReferenceStorageReferent();
360413
auto ret = IGF.emitTypeMetadataRef(substT);
361414
IGF.Builder.CreateRet(ret);
362415
}
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)