Skip to content

Commit 19f1ded

Browse files
committed
[move-only] When emitting an initializer for an empty struct, store an instance to make sure it is initialized.
I ran into this while fixing the parent commit when attempting to add the interpreter test in this commit into the aforementioned parent commit. rdar://107974302
1 parent 94fb5ce commit 19f1ded

File tree

3 files changed

+54
-1
lines changed

3 files changed

+54
-1
lines changed

lib/SILGen/SILGenConstructor.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,22 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
631631
if (!isDelegating) {
632632
auto *typeDC = ctor->getDeclContext();
633633
auto *nominal = typeDC->getSelfNominalTypeDecl();
634-
emitMemberInitializers(ctor, selfDecl, nominal);
634+
635+
// If we have an empty move only struct, then we will not initialize it with
636+
// any member initializers, breaking SIL. So in that case, just construct a
637+
// SIL struct value and initialize the memory with that.
638+
//
639+
// DISCUSSION: This only happens with noncopyable types since the memory
640+
// lifetime checker doesn't seem to process trivial locations. But empty
641+
// move only structs are non-trivial, so we need to handle this here.
642+
if (isa<StructDecl>(nominal) && nominal->isMoveOnly() &&
643+
nominal->getStoredProperties().empty()) {
644+
auto *si = B.createStruct(ctor, lowering.getLoweredType(), {});
645+
B.emitStoreValueOperation(ctor, si, selfLV.getLValueAddress(),
646+
StoreOwnershipQualifier::Init);
647+
} else {
648+
emitMemberInitializers(ctor, selfDecl, nominal);
649+
}
635650
}
636651

637652
emitProfilerIncrement(ctor->getTypecheckedBody());

test/Interpreter/moveonly.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,17 @@ Tests.test("deinit not called in init when assigned") {
8989
}
9090
expectEqual(0, FD2.count)
9191
}
92+
93+
Tests.test("empty struct") {
94+
@_moveOnly
95+
struct EmptyStruct {
96+
func doSomething() {}
97+
var value: Bool { false }
98+
}
99+
100+
let e = EmptyStruct()
101+
e.doSomething()
102+
if e.value {
103+
let _ = consume e
104+
}
105+
}

test/SILGen/moveonly.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,3 +779,27 @@ func checkMarkMustCheckOnCaptured(x: __owned FD) {
779779
func clodger<T>(_: () -> T) {}
780780
clodger({ consumeVal(x) })
781781
}
782+
783+
//////////////////
784+
// Empty Struct //
785+
//////////////////
786+
787+
@_moveOnly
788+
struct EmptyStruct {
789+
// Make sure we explicitly initialize empty struct as appropriate despite the
790+
// fact we do not have any fields.
791+
//
792+
// CHECK-LABEL: sil hidden [ossa] @$s8moveonly11EmptyStructVACycfC : $@convention(method) (@thin EmptyStruct.Type) -> @owned EmptyStruct {
793+
// CHECK: [[BOX:%.*]] = alloc_box ${ var EmptyStruct }, var, name "self"
794+
// CHECK: [[MARKED_UNINIT:%.*]] = mark_uninitialized [rootself] [[BOX]]
795+
// CHECK: [[PROJECT:%.*]] = project_box [[MARKED_UNINIT]]
796+
// CHECK: [[STRUCT:%.*]] = struct $EmptyStruct ()
797+
// CHECK: store [[STRUCT]] to [init] [[PROJECT]]
798+
// CHECK: [[MV_CHECK:%.*]] = mark_must_check [assignable_but_not_consumable] [[PROJECT]]
799+
// CHECK: [[LOADED_VALUE:%.*]] = load [copy] [[MV_CHECK]]
800+
// CHECK: destroy_value [[MARKED_UNINIT]]
801+
// CHECK: return [[LOADED_VALUE]]
802+
// CHECK: } // end sil function '$s8moveonly11EmptyStructVACycfC'
803+
init() {
804+
}
805+
}

0 commit comments

Comments
 (0)