Skip to content

IRGen: EmptyBoxType's representation cannot be nil because of a conflict with extra inhabitant assumption in indirect enums #10326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/Runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Runtime/HeapObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -1761,6 +1761,9 @@ struct TargetHeapLocalVariableMetadata
static bool classof(const TargetMetadata<Runtime> *metadata) {
return metadata->getKind() == MetadataKind::HeapLocalVariable;
}
constexpr TargetHeapLocalVariableMetadata()
: TargetHeapMetadata<Runtime>(MetadataKind::HeapLocalVariable),
OffsetToFirstCapture(0), CaptureDescription(nullptr) {}
};
using HeapLocalVariableMetadata
= TargetHeapLocalVariableMetadata<InProcess>;
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 6 additions & 2 deletions lib/IRGen/GenHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,7 @@ class EmptyBoxTypeInfo final : public BoxTypeInfo {
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
Expand Down Expand Up @@ -1584,7 +1584,11 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) {
// For fixed-sized types, we can emit concrete box metadata.
auto &fixedTI = cast<FixedTypeInfo>(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);
Expand Down
14 changes: 14 additions & 0 deletions lib/IRGen/IRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<llvm::Value *> args) {
auto cc = IGF.IGM.DefaultCC;
Expand Down
2 changes: 2 additions & 0 deletions lib/IRGen/IRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ class IRGenFunction {

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
Expand Down
7 changes: 7 additions & 0 deletions stdlib/public/SwiftShims/GlobalObjects.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions stdlib/public/runtime/HeapObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <cstdio>
#include <cstdlib>
#include <thread>
#include "../SwiftShims/GlobalObjects.h"
#include "../SwiftShims/RuntimeShims.h"
#if SWIFT_OBJC_INTEROP
# include <objc/NSObject.h>
Expand Down Expand Up @@ -227,6 +228,12 @@ OpaqueValue *swift::swift_projectBox(HeapObject *o) {
return metadata->project(o);
}

HeapObject *swift::swift_allocEmptyBox() {
auto heapObject = reinterpret_cast<HeapObject*>(&_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);
Expand Down
16 changes: 16 additions & 0 deletions stdlib/public/stubs/GlobalObjects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 1 addition & 2 deletions test/IRGen/access_markers.sil
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()) -> ()
Expand Down
3 changes: 2 additions & 1 deletion test/IRGen/partial_apply.sil
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
45 changes: 45 additions & 0 deletions test/Interpreter/enum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")