Skip to content

Commit d0ee902

Browse files
committed
NCGenerics: fix runtime demangling strategy
We generally should use the open-coded, metadata function "accessor" strategy when targeting older runtimes, but if the type is part of the stdlib, assume the runtime demangler will produce correct metadata, as the retrofitting of Optional, etc, was done Carefully. resolves rdar://131337585
1 parent 9d2af57 commit d0ee902

File tree

3 files changed

+231
-11
lines changed

3 files changed

+231
-11
lines changed

lib/IRGen/GenReflection.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,25 @@ getRuntimeVersionThatSupportsDemanglingType(CanType type) {
226226
// involving them.
227227
}
228228

229+
/// Any nominal type that has an inverse requirement in its generic
230+
/// signature uses NoncopyableGenerics. Since inverses are mangled into
231+
/// symbols, a Swift 6.0+ runtime is generally needed to demangle them.
232+
///
233+
/// We make an exception for types in the stdlib, like Optional, since the
234+
/// runtime should still be able to demangle them, based on the availability
235+
/// of the type.
236+
if (auto nominalTy = dyn_cast<NominalOrBoundGenericNominalType>(t)) {
237+
auto *nom = nominalTy->getDecl();
238+
if (auto sig = nom->getGenericSignature()) {
239+
SmallVector<InverseRequirement, 2> inverses;
240+
SmallVector<Requirement, 2> reqs;
241+
sig->getRequirementsWithInverses(reqs, inverses);
242+
if (!inverses.empty() && !nom->getModuleContext()->isStdlibModule()) {
243+
return addRequirement(Swift_6_0);
244+
}
245+
}
246+
}
247+
229248
return false;
230249
});
231250

test/IRGen/noncopyable_field_descriptors.swift

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-frontend -emit-ir -o - %s -module-name test \
2+
// RUN: %swift-frontend -emit-ir -o - %s -module-name test \
33
// RUN: -enable-experimental-feature NonescapableTypes \
44
// RUN: -parse-as-library \
55
// RUN: -enable-library-evolution \
6-
// RUN: > %t/test.irgen
6+
// RUN: -target %target-cpu-apple-macosx15 \
7+
// RUN: > %t/test_new.irgen
78

8-
// RUN: %FileCheck %s < %t/test.irgen
9+
// RUN: %swift-frontend -emit-ir -o - %s -module-name test \
10+
// RUN: -enable-experimental-feature NonescapableTypes \
11+
// RUN: -parse-as-library \
12+
// RUN: -enable-library-evolution \
13+
// RUN: -target %target-cpu-apple-macosx14 \
14+
// RUN: > %t/test_old.irgen
15+
16+
// RUN: %FileCheck --check-prefix=NEW %s < %t/test_new.irgen
17+
// RUN: %FileCheck --check-prefix=OLD %s < %t/test_old.irgen
918

1019
// rdar://124401253
1120
// REQUIRES: OS=macosx || OS=linux || OS=windows-msvc
@@ -28,14 +37,78 @@ public enum NeverCopyable<Wrapped: ~Copyable>: ~Copyable {
2837
@frozen
2938
public struct NonCopyable: ~Copyable { }
3039

31-
// CHECK: @"$s4test1CCMF" =
32-
// CHECK-SAME: @"symbolic _____yxG 4test21ConditionallyCopyableOAARi_zrlE"
33-
// CHECK-SAME: @"get_type_metadata Ri_zl4test21ConditionallyCopyableOyAA03NonC0VG.3"
34-
// CHECK-SAME: @"symbolic _____yxG 4test21ConditionallyCopyableOAARi_zrlE"
35-
// CHECK-SAME: @"get_type_metadata Ri_zl4test21ConditionallyCopyableOyAA03NonC0VG.3"
36-
public class C<T: ~Copyable> {
40+
// HINT: when debugging this test, you can look for an `i32 2` field in the
41+
// 'MF' constant as a separator that precedes each field descriptor.
42+
43+
// NEW: @"$s4test8CC_TestsCMF" =
44+
// NEW-SAME: @"symbolic _____yxG 4test21ConditionallyCopyableOAARi_zrlE"
45+
// NEW-SAME: @"symbolic _____yq_G 4test21ConditionallyCopyableOAARi_zrlE"
46+
// NEW-SAME: @"get_type_metadata Ri_zr0_l4test21ConditionallyCopyableOyAA03NonC0VG.3"
47+
// NEW-SAME: @"symbolic _____ySSG 4test21ConditionallyCopyableOAARi_zrlE"
48+
49+
// OLD: @"$s4test8CC_TestsCMF" =
50+
// OLD-SAME: @"get_type_metadata Ri_zr0_l4test21ConditionallyCopyableOyxG.3"
51+
// OLD-SAME: @"get_type_metadata Ri_zr0_l4test21ConditionallyCopyableOyq_G.4"
52+
// OLD-SAME: @"get_type_metadata Ri_zr0_l4test21ConditionallyCopyableOyAA03NonC0VG.5"
53+
// OLD-SAME: @"get_type_metadata Ri_zr0_l4test21ConditionallyCopyableOySSG.6"
54+
public class CC_Tests<NCG: ~Copyable, T> {
55+
var ccNCG: ConditionallyCopyable<NCG> = .none
3756
var ccT: ConditionallyCopyable<T> = .none
3857
var ccNC: ConditionallyCopyable<NonCopyable> = .none
39-
var ncT: ConditionallyCopyable<T> = .none
40-
var ncNC: ConditionallyCopyable<NonCopyable> = .none
58+
var ccC: ConditionallyCopyable<String> = .none
59+
}
60+
61+
62+
/// For the "never copyable" fields, we expect to always go through the
63+
/// type metadata accessor strategy, which is designed to hide these
64+
/// fields until a future runtime says they're safe to reflect.
65+
66+
// NEW: @"$s4test8NC_TestsCMF" =
67+
// NEW-SAME: @"get_type_metadata Ri_zr0_l4test13NeverCopyableOyxG.4"
68+
// NEW-SAME: @"get_type_metadata Ri_zr0_l4test13NeverCopyableOyq_G.5"
69+
// NEW-SAME: @"get_type_metadata Ri_zr0_l4test13NeverCopyableOyAA03NonC0VG.6"
70+
// NEW-SAME: @"get_type_metadata Ri_zr0_l4test13NeverCopyableOySSG.7"
71+
72+
// OLD: @"$s4test8NC_TestsCMF" =
73+
// OLD-SAME: @"get_type_metadata Ri_zr0_l4test13NeverCopyableOyxG.7"
74+
// OLD-SAME: @"get_type_metadata Ri_zr0_l4test13NeverCopyableOyq_G.8"
75+
// OLD-SAME: @"get_type_metadata Ri_zr0_l4test13NeverCopyableOyAA03NonC0VG.9"
76+
// OLD-SAME: @"get_type_metadata Ri_zr0_l4test13NeverCopyableOySSG.10"
77+
public class NC_Tests<NCG: ~Copyable, T> {
78+
var ncNCG: NeverCopyable<NCG> = .none
79+
var ncT: NeverCopyable<T> = .none
80+
var ncNC: NeverCopyable<NonCopyable> = .none
81+
var ncC: NeverCopyable<String> = .none
82+
}
83+
84+
85+
// NEW: @"$s4test17StdlibTypes_TestsCMF" =
86+
// NEW-SAME: @"symbolic xSg"
87+
// NEW-SAME: @"symbolic q_Sg"
88+
// NEW-SAME: @"get_type_metadata Ri_zRi__r0_l4test11NonCopyableVSg.8"
89+
// NEW-SAME: @"symbolic SSSg"
90+
// NEW-SAME: @"symbolic SPyxG"
91+
// NEW-SAME: @"symbolic SPyq_G"
92+
// NEW-SAME: @"symbolic SPy_____G 4test11NonCopyableV"
93+
// NEW-SAME: @"symbolic SPySSG"
94+
95+
// OLD: @"$s4test17StdlibTypes_TestsCMF" =
96+
// OLD-SAME: @"symbolic xSg"
97+
// OLD-SAME: @"symbolic q_Sg"
98+
// OLD-SAME: @"get_type_metadata Ri_zRi__r0_l4test11NonCopyableVSg.11"
99+
// OLD-SAME: @"symbolic SSSg"
100+
// OLD-SAME: @"symbolic SPyxG"
101+
// OLD-SAME: @"symbolic SPyq_G"
102+
// OLD-SAME: @"symbolic SPy_____G 4test11NonCopyableV"
103+
// OLD-SAME: @"symbolic SPySSG"
104+
public class StdlibTypes_Tests<NCG: ~Copyable, T: ~Copyable> {
105+
var optNCG: Optional<NCG> = .none
106+
var optT: Optional<T> = .none
107+
var optNC: Optional<NonCopyable> = .none
108+
var optC: Optional<String> = .none
109+
110+
var upNCG: UnsafePointer<NCG> = .init(bitPattern: 16)!
111+
var upT: UnsafePointer<T> = .init(bitPattern: 32)!
112+
var upNC: UnsafePointer<NonCopyable> = .init(bitPattern: 64)!
113+
var upC: UnsafePointer<String> = .init(bitPattern: 128)!
41114
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %swift-frontend %s -target %target-cpu-apple-macosx15 -module-name main -emit-ir -o %t/new.ir
3+
// RUN: %target-swift-frontend %s -target %target-cpu-apple-macosx11 -module-name main -emit-ir -o %t/old.ir
4+
5+
// RUN: %FileCheck %s --check-prefix=NEW < %t/new.ir
6+
// RUN: %FileCheck %s --check-prefix=OLD < %t/old.ir
7+
8+
// Check that we add extra type metadata accessors for types with generic
9+
// parameters that have an inverse. These are used instead of using demangling
10+
// cache variables since old runtimes cannot synthesize type metadata based on
11+
// the new mangling.
12+
13+
// RUN: %target-build-swift -target %target-cpu-apple-macosx14 %s -o %t/test_mangling
14+
// RUN: %target-run %t/test_mangling | %FileCheck %s
15+
16+
// REQUIRES: OS=macosx
17+
// REQUIRES: executable_test
18+
19+
20+
// This type's generic parameter is noncopyable, so older runtimes can't
21+
// demangle the type's name to build the metadata.
22+
struct Foo<T: ~Copyable>: ~Copyable {
23+
mutating func bar(_ i: Int) { print("Foo.bar(\(i))") }
24+
}
25+
26+
func test() {
27+
var foo = Foo<Int>()
28+
foo.bar(1)
29+
}
30+
test()
31+
// CHECK: Foo.bar(1)
32+
33+
// NEW: define hidden swiftcc void @"$s4main4testyyF"()
34+
// NEW: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main3FooVySiGMD")
35+
// NEW: }
36+
37+
// OLD: define hidden swiftcc void @"$s4main4testyyF"()
38+
// OLD: call swiftcc %swift.metadata_response @"$s4main3FooVySiGMa"(i64 0)
39+
// OLD: }
40+
41+
struct NC: ~Copyable {}
42+
43+
// NEW: define hidden swiftcc void @"$s4main10testWithNCyyF"()
44+
// NEW: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main3FooVyAA2NCVGMD")
45+
// NEW: }
46+
47+
// OLD: define hidden swiftcc void @"$s4main10testWithNCyyF"()
48+
// OLD: call swiftcc %swift.metadata_response @"$s4main3FooVyAA2NCVGMa"
49+
// OLD: }
50+
func testWithNC() {
51+
var foo = Foo<NC>()
52+
foo.bar(2)
53+
}
54+
testWithNC()
55+
// CHECK: Foo.bar(2)
56+
57+
58+
// NEW: define hidden swiftcc void @"$s4main17testWithNCGenericyyxnRi_zlF"
59+
// NEW: call swiftcc %swift.metadata_response @"$s4main3FooVMa"
60+
// NEW: }
61+
62+
// OLD: define hidden swiftcc void @"$s4main17testWithNCGenericyyxnRi_zlF"
63+
// OLD: call swiftcc %swift.metadata_response @"$s4main3FooVMa"
64+
// OLD: }
65+
func testWithNCGeneric<T: ~Copyable>(_ t: consuming T) {
66+
var foo = Foo<T>()
67+
foo.bar(3)
68+
}
69+
testWithNCGeneric(Foo<NC>())
70+
// CHECK: Foo.bar(3)
71+
72+
73+
// This type does not need a Swift 6.0 runtime, despite being noncopyable,
74+
// because it doesn't have a noncopyable generic parameter.
75+
struct JustNoncopyable<T>: ~Copyable {
76+
mutating func bar() { print("JustNoncopyable.bar") }
77+
}
78+
79+
func testNonGeneric() {
80+
var ng = JustNoncopyable<Int>()
81+
ng.bar()
82+
}
83+
testNonGeneric()
84+
// CHECK: JustNoncopyable.bar
85+
86+
// NEW: define hidden swiftcc void @"$s4main14testNonGenericyyF"()
87+
// NEW: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main15JustNoncopyableVySiGMD")
88+
// NEW: }
89+
90+
// OLD: define hidden swiftcc void @"$s4main14testNonGenericyyF"()
91+
// OLD: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main15JustNoncopyableVySiGMD")
92+
// OLD: }
93+
94+
95+
/// Check that Optional still uses `__swift_instantiateConcreteTypeFromMangledName`
96+
/// even when calling a method available to a noncopyable Optional.
97+
extension Optional where Wrapped: ~Copyable {
98+
mutating func bar(_ i: Int) { print("Optional.bar(\(i))") }
99+
}
100+
101+
// NEW: define hidden swiftcc void @"$s4main20testCopyableOptionalyyF"()
102+
// NEW: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$sSiSgMD")
103+
// NEW: }
104+
105+
// OLD: define hidden swiftcc void @"$s4main20testCopyableOptionalyyF"()
106+
// OLD: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$sSiSgMD")
107+
// OLD: }
108+
func testCopyableOptional() {
109+
var opt = Optional<Int>(94103)
110+
opt.bar(1)
111+
}
112+
testCopyableOptional()
113+
// CHECK: Optional.bar(1)
114+
115+
116+
// NEW: define hidden swiftcc void @"$s4main23testNOTCopyableOptionalyyF"()
117+
// NEW: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main2NCVSgMD")
118+
// NEW: }
119+
120+
// OLD: define hidden swiftcc void @"$s4main23testNOTCopyableOptionalyyF"()
121+
// OLD: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main2NCVSgMD")
122+
// OLD: }
123+
func testNOTCopyableOptional() {
124+
var opt = Optional<NC>(NC())
125+
opt.bar(2)
126+
}
127+
testNOTCopyableOptional()
128+
// CHECK: Optional.bar(2)

0 commit comments

Comments
 (0)