From 48e889b51ba4302f948c5d91d148b96a1919dae6 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Sat, 17 Jun 2017 09:33:41 -0700 Subject: [PATCH] IRGen: EmptyBoxType's representation cannot be nil because of a conflict with extra inhabitant assumption in indirect enums (#10326) * IRGen: EmptyBoxType's representation cannot be nil because of a conflict with extra inhabitant assumption in indirect enums We map nil to the .None case of Optional. Instead use a singleton object. SR-5148 rdar://32618580 --- docs/Runtime.md | 1 + include/swift/Runtime/HeapObject.h | 4 ++ include/swift/Runtime/Metadata.h | 3 ++ include/swift/Runtime/RuntimeFunctions.def | 5 +++ lib/IRGen/GenHeap.cpp | 8 +++- lib/IRGen/IRGenFunction.cpp | 14 +++++++ lib/IRGen/IRGenFunction.h | 2 + stdlib/public/SwiftShims/GlobalObjects.h | 7 ++++ stdlib/public/runtime/HeapObject.cpp | 7 ++++ stdlib/public/stubs/GlobalObjects.cpp | 16 ++++++++ test/IRGen/access_markers.sil | 3 +- test/IRGen/partial_apply.sil | 3 +- test/Interpreter/enum.swift | 45 ++++++++++++++++++++++ 13 files changed, 113 insertions(+), 5 deletions(-) diff --git a/docs/Runtime.md b/docs/Runtime.md index 2da081396fb..6d62b460b77 100644 --- a/docs/Runtime.md +++ b/docs/Runtime.md @@ -71,6 +71,7 @@ Rename with a non-`stdlib` naming scheme. ``` 000000000001cb30 T _swift_allocBox +000000000001cb30 T _swift_allocEmptyBox 000000000001c990 T _swift_allocObject 000000000001ca60 T _swift_bufferAllocate 000000000001ca90 T _swift_bufferHeaderSize diff --git a/include/swift/Runtime/HeapObject.h b/include/swift/Runtime/HeapObject.h index 66d7198f676..b4c1ae08e4b 100644 --- a/include/swift/Runtime/HeapObject.h +++ b/include/swift/Runtime/HeapObject.h @@ -169,6 +169,10 @@ SWIFT_RUNTIME_EXPORT BoxPair::Return swift_makeBoxUnique(OpaqueValue *buffer, Metadata const *type, size_t alignMask); +/// Returns the address of a heap object representing all empty box types. +SWIFT_RUNTIME_EXPORT +HeapObject* swift_allocEmptyBox(); + // Allocate plain old memory. This is the generalized entry point // Never returns nil. The returned memory is uninitialized. // diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index 9a2d1efe86d..285a911673a 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -1761,6 +1761,9 @@ struct TargetHeapLocalVariableMetadata static bool classof(const TargetMetadata *metadata) { return metadata->getKind() == MetadataKind::HeapLocalVariable; } + constexpr TargetHeapLocalVariableMetadata() + : TargetHeapMetadata(MetadataKind::HeapLocalVariable), + OffsetToFirstCapture(0), CaptureDescription(nullptr) {} }; using HeapLocalVariableMetadata = TargetHeapLocalVariableMetadata; diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 75c885b93b0..8305984ec8d 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -73,6 +73,11 @@ FUNCTION(ProjectBox, swift_projectBox, DefaultCC, ARGS(RefCountedPtrTy), ATTRS(NoUnwind, ReadNone)) +FUNCTION(AllocEmptyBox, swift_allocEmptyBox, DefaultCC, + RETURNS(RefCountedPtrTy), + ARGS(), + ATTRS(NoUnwind)) + // RefCounted *swift_allocObject(Metadata *type, size_t size, size_t alignMask); FUNCTION_WITH_GLOBAL_SYMBOL_AND_IMPL(AllocObject, swift_allocObject, _swift_allocObject, _swift_allocObject_, RegisterPreservingCC, diff --git a/lib/IRGen/GenHeap.cpp b/lib/IRGen/GenHeap.cpp index 6220aebb54a..c1421e1f6ec 100644 --- a/lib/IRGen/GenHeap.cpp +++ b/lib/IRGen/GenHeap.cpp @@ -1430,7 +1430,7 @@ public: allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env, const llvm::Twine &name) const override { return OwnedAddress(IGF.getTypeInfo(boxedType).getUndefAddress(), - IGF.IGM.RefCountedNull); + IGF.emitAllocEmptyBoxCall()); } void @@ -1584,7 +1584,11 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) { // For fixed-sized types, we can emit concrete box metadata. auto &fixedTI = cast(eltTI); - // For empty types, we don't really need to allocate anything. + // Because we assume in enum's that payloads with a Builtin.NativeObject which + // is also the type for indirect enum cases have extra inhabitants of pointers + // we can't have a nil pointer as a representation for an empty box type -- + // nil conflicts with the extra inhabitants. We return a static singleton + // empty box object instead. if (fixedTI.isKnownEmpty(ResilienceExpansion::Maximal)) { if (!EmptyBoxTI) EmptyBoxTI = new EmptyBoxTypeInfo(IGM); diff --git a/lib/IRGen/IRGenFunction.cpp b/lib/IRGen/IRGenFunction.cpp index 0b91a764b08..e9a95db879c 100644 --- a/lib/IRGen/IRGenFunction.cpp +++ b/lib/IRGen/IRGenFunction.cpp @@ -209,6 +209,20 @@ llvm::Value *IRGenFunction::emitProjectBoxCall(llvm::Value *box, return call; } +llvm::Value *IRGenFunction::emitAllocEmptyBoxCall() { + llvm::Attribute::AttrKind attrKinds[] = { + llvm::Attribute::NoUnwind, + }; + auto attrs = llvm::AttributeSet::get(IGM.LLVMContext, + llvm::AttributeSet::FunctionIndex, + attrKinds); + llvm::CallInst *call = + Builder.CreateCall(IGM.getAllocEmptyBoxFn(), {}); + call->setCallingConv(IGM.DefaultCC); + call->setAttributes(attrs); + return call; +} + static void emitDeallocatingCall(IRGenFunction &IGF, llvm::Constant *fn, std::initializer_list args) { auto cc = IGF.IGM.DefaultCC; diff --git a/lib/IRGen/IRGenFunction.h b/lib/IRGen/IRGenFunction.h index d28a0926b9d..ab552dcbd33 100644 --- a/lib/IRGen/IRGenFunction.h +++ b/lib/IRGen/IRGenFunction.h @@ -188,6 +188,8 @@ public: llvm::Value *emitProjectBoxCall(llvm::Value *box, llvm::Value *typeMetadata); + llvm::Value *emitAllocEmptyBoxCall(); + // Emit a reference to the canonical type metadata record for the given AST // type. This can be used to identify the type at runtime. For types with // abstraction difference, the metadata contains the layout information for diff --git a/stdlib/public/SwiftShims/GlobalObjects.h b/stdlib/public/SwiftShims/GlobalObjects.h index ad1aa18877f..ace12a15499 100644 --- a/stdlib/public/SwiftShims/GlobalObjects.h +++ b/stdlib/public/SwiftShims/GlobalObjects.h @@ -88,6 +88,13 @@ struct _SwiftHashingSecretKey _swift_stdlib_Hashing_secretKey; SWIFT_RUNTIME_STDLIB_INTERFACE __swift_uint64_t _swift_stdlib_HashingDetail_fixedSeedOverride; +struct _SwiftEmptyBoxStorage { + struct HeapObject header; +}; + +SWIFT_RUNTIME_STDLIB_INTERFACE +struct _SwiftEmptyBoxStorage _EmptyBoxStorage; + #ifdef __cplusplus static_assert(std::is_pod<_SwiftEmptyArrayStorage>::value, diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index c9a311ca62b..f3ae70fa0b9 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -31,6 +31,7 @@ #include #include #include +#include "../SwiftShims/GlobalObjects.h" #include "../SwiftShims/RuntimeShims.h" #if SWIFT_OBJC_INTEROP # include @@ -227,6 +228,12 @@ OpaqueValue *swift::swift_projectBox(HeapObject *o) { return metadata->project(o); } +HeapObject *swift::swift_allocEmptyBox() { + auto heapObject = reinterpret_cast(&_EmptyBoxStorage); + SWIFT_RT_ENTRY_CALL(swift_retain)(heapObject); + return heapObject; +} + // Forward-declare this, but define it after swift_release. extern "C" LLVM_LIBRARY_VISIBILITY LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED void _swift_release_dealloc(HeapObject *object) SWIFT_CC(RegisterPreservingCC_IMPL); diff --git a/stdlib/public/stubs/GlobalObjects.cpp b/stdlib/public/stubs/GlobalObjects.cpp index 6c5b30dfc8f..d7ee353e36f 100644 --- a/stdlib/public/stubs/GlobalObjects.cpp +++ b/stdlib/public/stubs/GlobalObjects.cpp @@ -35,6 +35,10 @@ ClassMetadata CLASS_METADATA_SYM(s27_RawNativeDictionaryStorage); // _direct type metadata for Swift._RawNativeSetStorage SWIFT_RUNTIME_STDLIB_INTERFACE ClassMetadata CLASS_METADATA_SYM(s20_RawNativeSetStorage); + +// _direct type metadata for Swift._EmptyBoxStorage +SWIFT_RUNTIME_STDLIB_INTERFACE +ClassMetadata CLASS_METADATA_SYM(s16_EmptyBoxStorage); } // namespace swift swift::_SwiftEmptyArrayStorage swift::_swiftEmptyArrayStorage = { @@ -127,6 +131,18 @@ void swift::_swift_instantiateInertHeapObject(void *address, ::new (address) HeapObject{metadata}; } +swift::HeapLocalVariableMetadata _emptyBoxStorageMetadata; + +/// The signleton empty box storage object. +swift::_SwiftEmptyBoxStorage swift::_EmptyBoxStorage = { + // HeapObject header; + { + &_emptyBoxStorageMetadata, + //&swift::CLASS_METADATA_SYM(s16_EmptyBoxStorage), // isa pointer + } +}; + + namespace llvm { namespace hashing { namespace detail { // An extern variable expected by LLVM's hashing templates. We don't link any // LLVM libs into the runtime, so define this here. diff --git a/test/IRGen/access_markers.sil b/test/IRGen/access_markers.sil index 40b2deed050..06f1b70c5a2 100644 --- a/test/IRGen/access_markers.sil +++ b/test/IRGen/access_markers.sil @@ -114,8 +114,7 @@ sil @testPairedBox : $(@guaranteed { var () }) -> () { bb0(%0 : ${ var () }): // CHECK: entry: %2 = project_box %0 : ${ var () }, 0 - - // CHECK-NEXT: call {{.*}}void @writeEmptyTuple(%swift.opaque* nocapture undef) + // CHECK-NEXT: call {{.*}}void @writeEmptyTuple(%swift.opaque* nocapture undef) %3 = begin_access [modify] [dynamic] %2 : $*() %write_fn = function_ref @writeEmptyTuple : $@convention(thin) (@inout ()) -> () apply %write_fn(%3) : $@convention(thin) (@inout ()) -> () diff --git a/test/IRGen/partial_apply.sil b/test/IRGen/partial_apply.sil index 5c4af066cf9..c10892a1757 100644 --- a/test/IRGen/partial_apply.sil +++ b/test/IRGen/partial_apply.sil @@ -365,7 +365,8 @@ sil public_external @partial_empty_box : $@convention(thin) (@owned <τ_0_0> { v // CHECK-LABEL: define{{( protected)?}} swiftcc void @empty_box() sil @empty_box : $@convention(thin) () -> () { entry: - // CHECK: store %swift.refcounted* null + // CHECK: [[BOX:%.*]] = call {{.*}}swift_allocEmptyBox + // CHECK: store %swift.refcounted* [[BOX]] // CHECK: store %swift.opaque* undef %b = alloc_box $<τ_0_0> { var τ_0_0 } <()> %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <()>, 0 diff --git a/test/Interpreter/enum.swift b/test/Interpreter/enum.swift index 50c9eb8e10b..225af0551cb 100644 --- a/test/Interpreter/enum.swift +++ b/test/Interpreter/enum.swift @@ -553,5 +553,50 @@ presentEitherOr(EitherOr<(), String>.Right("foo")) // CHECK-NEXT: Right(foo) // CHECK-NEXT: Right(foo) presentEitherOrsOf(t: (), u: "foo") +// SR-5148 +enum Payload { + case email +} +enum Test { + case a + indirect case b(Payload) +} + +@inline(never) +func printA() { + print("an a") +} + +@inline(never) +func printB() { + print("an b") +} + +@inline(never) +func testCase(_ testEmail: Test) { + switch testEmail { + case .a: + printA() + case .b: + printB() + } +} + +@inline(never) +func createTestB() -> Test { + return Test.b(.email) +} + +@inline(never) +func createTestA() -> Test { + return Test.a +} + +// CHECK-NEXT: an b +testCase(createTestB()) +// CHECK-NEXT: b(a.Payload.email) +print(createTestB()) +// CHECK-NEXT: a +print(createTestA()) // CHECK-NEXT: done print("done")