Skip to content

Commit 730c5cf

Browse files
authored
Merge pull request #64295 from jckarter/noncopyable-hide-field-types
Don't let reflection handle noncopyable types yet.
2 parents 1924376 + 4d6d765 commit 730c5cf

File tree

6 files changed

+131
-4
lines changed

6 files changed

+131
-4
lines changed

lib/IRGen/GenReflection.cpp

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,54 @@ getTypeRefByFunction(IRGenModule &IGM,
264264
? genericEnv->mapTypeIntoContext(t)->getCanonicalType()
265265
: t;
266266

267+
// If a type is noncopyable, lie about the resolved type unless the
268+
// runtime is sufficiently aware of noncopyable types.
269+
if (substT->isPureMoveOnly()) {
270+
// Darwin-based platforms have ABI stability, and we want binaries
271+
// that use noncopyable types nongenerically today to be forward
272+
// compatible with a future OS runtime that supports noncopyable
273+
// generics. On other platforms, a new Swift compiler and runtime
274+
// require recompilation anyway, so this dance is unnecessary, and
275+
// for now, we can unconditionally lie.
276+
bool useForwardCompatibility =
277+
IGM.Context.LangOpts.Target.isOSDarwin();
278+
279+
llvm::Instruction *br = nullptr;
280+
llvm::BasicBlock *supportedBB = nullptr;
281+
if (useForwardCompatibility) {
282+
auto runtimeSupportsNoncopyableTypesSymbol
283+
= IGM.Module.getOrInsertGlobal("swift_runtimeSupportsNoncopyableTypes",
284+
IGM.Int8Ty);
285+
cast<llvm::GlobalVariable>(runtimeSupportsNoncopyableTypesSymbol)
286+
->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
287+
288+
auto runtimeSupportsNoncopyableTypes
289+
= IGF.Builder.CreateIsNotNull(runtimeSupportsNoncopyableTypesSymbol,
290+
"supports.noncopyable");
291+
supportedBB = IGF.createBasicBlock("does.support.noncopyable");
292+
auto unsupportedBB = IGF.createBasicBlock("does.not.support.noncopyable");
293+
br = IGF.Builder.CreateCondBr(runtimeSupportsNoncopyableTypes,
294+
supportedBB,
295+
unsupportedBB);
296+
297+
IGF.Builder.emitBlock(unsupportedBB);
298+
}
299+
300+
// If the runtime does not yet support noncopyable types, lie that the
301+
// field is an empty tuple, so the runtime doesn't try to do anything
302+
// with the actual value.
303+
auto phonyRet = IGF.emitTypeMetadataRef(IGM.Context.TheEmptyTupleType);
304+
IGF.Builder.CreateRet(phonyRet);
305+
306+
if (!useForwardCompatibility) {
307+
goto done_building_function;
308+
}
309+
310+
// Emit the type metadata normally otherwise.
311+
IGF.Builder.SetInsertPoint(br);
312+
IGF.Builder.emitBlock(supportedBB);
313+
}
314+
267315
bindFromGenericRequirementsBuffer(
268316
IGF, requirements,
269317
Address(bindingsBufPtr, IGM.Int8Ty, IGM.getPointerAlignment()),
@@ -276,6 +324,7 @@ getTypeRefByFunction(IRGenModule &IGM,
276324
auto ret = IGF.emitTypeMetadataRef(substT);
277325
IGF.Builder.CreateRet(ret);
278326
}
327+
done_building_function:
279328
// Form the mangled name with its relative reference.
280329
auto S = B.beginStruct();
281330
S.setPacked(true);
@@ -315,6 +364,21 @@ getTypeRefImpl(IRGenModule &IGM,
315364
case MangledTypeRefRole::FlatUnique:
316365
useFlatUnique = true;
317366
break;
367+
368+
case MangledTypeRefRole::FieldMetadata:
369+
// We want to keep fields of noncopyable type from being exposed to
370+
// in-process runtime reflection libraries in older Swift runtimes, since
371+
// they more than likely assume they can copy field values, and the language
372+
// support for noncopyable types as dynamic or generic types isn't yet
373+
// implemented as of the writing of this comment. If the type is
374+
// noncopyable, use a function to emit the type ref which will look for a
375+
// signal from future runtimes whether they support noncopyable types before
376+
// exposing their metadata to them.
377+
if (type->isPureMoveOnly()) {
378+
IGM.IRGen.noteUseOfTypeMetadata(type);
379+
return getTypeRefByFunction(IGM, sig, type);
380+
}
381+
LLVM_FALLTHROUGH;
318382

319383
case MangledTypeRefRole::DefaultAssociatedTypeWitness:
320384
case MangledTypeRefRole::Metadata:
@@ -724,14 +788,14 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder {
724788
if (type->isForeignReferenceType()) {
725789
auto opaqueType = type->getASTContext().getOpaquePointerType();
726790
// The standard library's Mirror demangles metadata from field
727-
// descriptors, so use MangledTypeRefRole::Metadata to ensure
791+
// descriptors, so use MangledTypeRefRole::FieldMetadata to ensure
728792
// runtime metadata is available.
729-
addTypeRef(opaqueType, genericSig, MangledTypeRefRole::Metadata);
793+
addTypeRef(opaqueType, genericSig, MangledTypeRefRole::FieldMetadata);
730794
} else {
731795
// The standard library's Mirror demangles metadata from field
732-
// descriptors, so use MangledTypeRefRole::Metadata to ensure
796+
// descriptors, so use MangledTypeRefRole::FieldMetadata to ensure
733797
// runtime metadata is available.
734-
addTypeRef(type, genericSig, MangledTypeRefRole::Metadata);
798+
addTypeRef(type, genericSig, MangledTypeRefRole::FieldMetadata);
735799
}
736800
}
737801

lib/IRGen/IRGenMangler.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ mangleSymbolNameForSymbolicMangling(const SymbolicMangling &mangling,
300300
prefix = "default assoc type ";
301301
break;
302302

303+
case MangledTypeRefRole::FieldMetadata:
303304
case MangledTypeRefRole::Metadata:
304305
case MangledTypeRefRole::Reflection:
305306
prefix = "symbolic ";

lib/IRGen/IRGenModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,10 @@ class TypeEntityReference {
575575

576576
/// Describes the role of a mangled type reference string.
577577
enum class MangledTypeRefRole {
578+
/// The mangled type reference is used for field metadata, which is used
579+
/// by both in-process and out-of-process reflection, so still requires
580+
/// referenced types' metadata to be fully emitted.
581+
FieldMetadata,
578582
/// The mangled type reference is used for normal metadata.
579583
Metadata,
580584
/// The mangled type reference is used for reflection metadata.

lib/IRGen/MetadataRequest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ llvm::Constant *IRGenModule::getAddrOfStringForTypeRef(
377377

378378
case MangledTypeRefRole::Metadata:
379379
case MangledTypeRefRole::Reflection:
380+
case MangledTypeRefRole::FieldMetadata:
380381
break;
381382
}
382383

stdlib/public/runtime/Metadata.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,19 @@ extern "C" void _objc_setClassCopyFixupHandler(void (* _Nonnull newFixupHandler)
7575
using namespace swift;
7676
using namespace metadataimpl;
7777

78+
#if defined(__APPLE__)
79+
// Binaries using noncopyable types check the address of the symbol
80+
// `swift_runtimeSupportsNoncopyableTypes` before exposing any noncopyable
81+
// type metadata through in-process reflection, to prevent existing code
82+
// that expects all types to be copyable from crashing or causing bad behavior
83+
// by copying noncopyable types. The runtime does not yet support noncopyable
84+
// types, so we explicitly define this symbol to be zero for now. Binaries
85+
// weak-import this symbol so they will resolve it to a zero address on older
86+
// runtimes as well.
87+
__asm__(" .globl _swift_runtimeSupportsNoncopyableTypes\n");
88+
__asm__(".set _swift_runtimeSupportsNoncopyableTypes, 0\n");
89+
#endif
90+
7891
// GenericParamDescriptor is a single byte, so while it's difficult to
7992
// imagine needing even a quarter this many generic params, there's very
8093
// little harm in doing it.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-move-only)
2+
// REQUIRES: executable_test
3+
4+
// Verify that iterating through the fields of an object whose class has
5+
// a move-only field does not trap from trying to reflect and copy those
6+
// move-only fields.
7+
8+
@_moveOnly
9+
public struct MO {
10+
var x: Int8 = 0
11+
var y: Int8 = 0
12+
var z: Int8 = 0
13+
14+
deinit { print("destroyed MO") }
15+
}
16+
17+
public class MOHaver {
18+
var s: String = "hello"
19+
var mo: MO = MO()
20+
var b: Int8 = 42
21+
var c: Any.Type = MOHaver.self
22+
}
23+
24+
// CHECK-LABEL: doing nongeneric
25+
print("doing nongeneric")
26+
do {
27+
let k = MOHaver()
28+
29+
let mirror = Mirror(reflecting: k)
30+
// CHECK-NEXT: s: hello
31+
// Whether this actually prints the value of `k.mo` or not is irrelevant
32+
// to the test; we care that attempting to reflect the field does not trap
33+
// copying a noncopyable field.
34+
// CHECK-NEXT: mo:
35+
// CHECK-NEXT: b: 42
36+
// CHECK-NEXT: c: {{.*}}.MOHaver
37+
for c in mirror.children {
38+
print("\(c.label!): \(c.value)")
39+
}
40+
// CHECK-NEXT: destroyed MO
41+
}
42+
// CHECK-NEXT: done with nongeneric
43+
print("done with nongeneric")
44+

0 commit comments

Comments
 (0)