Skip to content

Commit 4360c1f

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 84a0779 commit 4360c1f

File tree

6 files changed

+183
-6
lines changed

6 files changed

+183
-6
lines changed

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7003,7 +7003,7 @@ static void addGenericRequirement(IRGenModule &IGM, ConstantStructBuilder &B,
70037003
++metadata.NumGenericKeyArguments;
70047004

70057005
B.addInt(IGM.Int32Ty, flags.getIntValue());
7006-
addRelativeAddressOfTypeRef(IGM, B, paramType, nullptr);
7006+
addRelativeAddressOfTypeRef(IGM, B, paramType, sig);
70077007
addReference();
70087008
}
70097009

lib/IRGen/GenReflection.cpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ class PrintMetadataSource
176176
};
177177

178178
std::optional<llvm::VersionTuple>
179-
getRuntimeVersionThatSupportsDemanglingType(CanType type) {
179+
getRuntimeVersionThatSupportsDemanglingType(CanGenericSignature sig,
180+
CanType type) {
180181
enum VersionRequirement {
181182
None,
182183
Swift_5_2,
@@ -197,6 +198,19 @@ getRuntimeVersionThatSupportsDemanglingType(CanType type) {
197198
};
198199

199200
(void) type.findIf([&](CanType t) -> bool {
201+
if (auto gtpt = dyn_cast<GenericTypeParamType>(t)) {
202+
assert(sig && "no signature for generic parameter?");
203+
auto &ctx = sig->getASTContext();
204+
205+
// Check for conformance to all invertible protocols. If conformance is
206+
// missing, then we'd mangle the absence as an inverse requirement.
207+
for (auto ip : InvertibleProtocolSet::allKnown()) {
208+
auto kp = getKnownProtocolKind(ip);
209+
if (!sig->requiresProtocol(gtpt, ctx.getProtocol(kp)))
210+
return addRequirement(Swift_6_0);
211+
}
212+
}
213+
200214
if (auto fn = dyn_cast<AnyFunctionType>(t)) {
201215
// The Swift 6.0 runtime is the first version able to demangle types
202216
// that involve typed throws or @isolated(any), or for that matter
@@ -226,6 +240,39 @@ getRuntimeVersionThatSupportsDemanglingType(CanType type) {
226240
// involving them.
227241
}
228242

243+
// Any nominal type that has an inverse requirement in its generic signature
244+
// uses NoncopyableGenerics. Since inverses are mangled into symbols,
245+
// a Swift 6.0+ runtime is needed to demangle them.
246+
if (auto nominalTy = dyn_cast<NominalOrBoundGenericNominalType>(t)) {
247+
auto *nom = nominalTy->getDecl();
248+
if (auto sig = nom->getGenericSignature()) {
249+
SmallVector<InverseRequirement, 2> inverses;
250+
SmallVector<Requirement, 2> reqs;
251+
sig->getRequirementsWithInverses(reqs, inverses);
252+
if (!inverses.empty()) {
253+
if (!isa<BoundGenericType>(nominalTy))
254+
return addRequirement(Swift_6_0);
255+
256+
// If all of the generic arguments in this BGT are Copyable/Escapable,
257+
// then it should not require the newer runtime.
258+
auto boundTy = cast<BoundGenericType>(nominalTy);
259+
for (auto arg : boundTy->getGenericArgs()) {
260+
for (auto ip : InvertibleProtocolSet::allKnown()) {
261+
switch (ip) {
262+
case InvertibleProtocolKind::Copyable:
263+
if (arg->isNoncopyable())
264+
return addRequirement(Swift_6_0);
265+
break;
266+
case InvertibleProtocolKind::Escapable:
267+
if (!arg->isEscapable())
268+
return addRequirement(Swift_6_0);
269+
}
270+
}
271+
}
272+
}
273+
}
274+
}
275+
229276
return false;
230277
});
231278

@@ -358,6 +405,7 @@ getTypeRefByFunction(IRGenModule &IGM,
358405
Address(bindingsBufPtr, IGM.Int8Ty, IGM.getPointerAlignment()),
359406
MetadataState::Complete, subs);
360407

408+
substT = substT.getReferenceStorageReferent();
361409
auto ret = IGF.emitTypeMetadataRef(substT);
362410
IGF.Builder.CreateRet(ret);
363411
}
@@ -378,11 +426,12 @@ getTypeRefByFunction(IRGenModule &IGM,
378426
}
379427

380428
bool swift::irgen::mangledNameIsUnknownToDeployTarget(IRGenModule &IGM,
429+
CanGenericSignature sig,
381430
CanType type) {
382431
if (auto runtimeCompatVersion = getSwiftRuntimeCompatibilityVersionForTarget(
383432
IGM.Context.LangOpts.Target)) {
384433
if (auto minimumSupportedRuntimeVersion =
385-
getRuntimeVersionThatSupportsDemanglingType(type)) {
434+
getRuntimeVersionThatSupportsDemanglingType(sig, type)) {
386435
if (*runtimeCompatVersion < *minimumSupportedRuntimeVersion) {
387436
return true;
388437
}
@@ -450,7 +499,7 @@ getTypeRefImpl(IRGenModule &IGM,
450499
// If the minimum deployment target's runtime demangler wouldn't understand
451500
// this mangled name, then fall back to generating a "mangled name" with a
452501
// symbolic reference with a callback function.
453-
if (mangledNameIsUnknownToDeployTarget(IGM, type)) {
502+
if (mangledNameIsUnknownToDeployTarget(IGM, sig, type)) {
454503
return getTypeRefByFunction(IGM, sig, type);
455504
}
456505

lib/IRGen/IRGenMangler.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,9 @@ class IRGenMangler : public Mangle::ASTMangler {
728728
/// Determines if the minimum deployment target's runtime demangler will not
729729
/// understand the mangled name for the given type.
730730
/// \returns true iff the target's runtime does not understand the mangled name.
731-
bool mangledNameIsUnknownToDeployTarget(IRGenModule &IGM, CanType type);
731+
bool mangledNameIsUnknownToDeployTarget(IRGenModule &IGM,
732+
CanGenericSignature sig,
733+
CanType type);
732734

733735
} // end namespace irgen
734736
} // end namespace swift

lib/IRGen/MetadataRequest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2933,7 +2933,7 @@ static bool shouldAccessByMangledName(IRGenModule &IGM, CanType type) {
29332933
return false;
29342934

29352935
// Do not access by mangled name if the runtime won't understand it.
2936-
if (mangledNameIsUnknownToDeployTarget(IGM, type))
2936+
if (mangledNameIsUnknownToDeployTarget(IGM, /*sig=*/nullptr, type))
29372937
return false;
29382938

29392939
// A nongeneric nominal type with nontrivial metadata has an accessor
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: }
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-swift-frontend -emit-ir -target %target-cpu-apple-macos99.99 %s \
2+
// RUN: | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-PRESENT %s
3+
4+
// RUN: %target-swift-frontend -emit-ir -target %target-cpu-apple-macos14.4 %s \
5+
// RUN: | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-OLDER %s
6+
7+
// REQUIRES: OS=macosx
8+
// UNSUPPORTED: CPU=arm64e
9+
10+
public struct MyStruct<T: ~Copyable> {
11+
let fn: (borrowing T) -> ()
12+
}
13+
14+
// Make sure that we only emit a demangling-based type description
15+
// for noncopyable generic parameters when deploying to runtimes supporting it.
16+
17+
// CHECK-LABEL: @"$s31reflection_metadata_noncopyable8MyStructVMF" = internal constant
18+
// CHECK-PRESENT-SAME: ptr @"symbolic yxc"
19+
// CHECK-OLDER-SAME: ptr @"get_type_metadata Ri_zlyxc.2"

0 commit comments

Comments
 (0)