Skip to content

Commit ab59958

Browse files
authored
Merge pull request #65130 from gottesmm/pr-56cbe3c44dba95e1fd7880b174205c541e11c01d
[move-only] Add support for move only empty structs and fix a SILGen issue around their initializers.
2 parents e874e75 + 19f1ded commit ab59958

File tree

6 files changed

+565
-1
lines changed

6 files changed

+565
-1
lines changed

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ TypeSubElementCount::TypeSubElementCount(SILType type, SILModule &mod,
6969
numElements += TypeSubElementCount(
7070
type.getFieldType(fieldDecl, mod, context), mod, context);
7171
number = numElements;
72+
73+
// If we do not have any elements, just set our size to 1.
74+
if (numElements == 0)
75+
number = 1;
76+
7277
return;
7378
}
7479

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+
}

test/SILOptimizer/moveonly_addresschecker_diagnostics.swift

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2482,3 +2482,312 @@ func yieldTest() {
24822482
}
24832483
}
24842484
}
2485+
2486+
///////////////////////
2487+
// Empty Struct Test //
2488+
///////////////////////
2489+
2490+
@_moveOnly
2491+
struct EmptyStruct {
2492+
var bool: Bool { false }
2493+
func doSomething() {}
2494+
mutating func doSomething2() {}
2495+
consuming func doSomething3() {}
2496+
}
2497+
2498+
func borrow(_ x: borrowing EmptyStruct) {}
2499+
func consume(_ x: consuming EmptyStruct) {}
2500+
2501+
func testEmptyStruct() {
2502+
func testArg1(_ x: consuming EmptyStruct) {
2503+
borrow(x)
2504+
}
2505+
2506+
func testArg2(_ x: consuming EmptyStruct) {
2507+
consume(x)
2508+
}
2509+
2510+
func testArg2a(_ x: consuming EmptyStruct) {
2511+
// expected-error @-1 {{'x' consumed more than once}}
2512+
consume(x) // expected-note {{consuming use here}}
2513+
consume(x) // expected-note {{consuming use here}}
2514+
}
2515+
2516+
func testArg2b(_ x: consuming EmptyStruct) {
2517+
// expected-error @-1 {{'x' used after consume}}
2518+
borrow(x)
2519+
consume(x) // expected-note {{consuming use here}}
2520+
borrow(x) // expected-note {{non-consuming use here}}
2521+
}
2522+
2523+
func testArg3(_ x: consuming EmptyStruct) {
2524+
let _ = x
2525+
}
2526+
2527+
func testArg3a(_ x: consuming EmptyStruct) {
2528+
// expected-error @-1 {{'x' consumed more than once}}
2529+
let _ = x // expected-note {{consuming use here}}
2530+
let _ = x // expected-note {{consuming use here}}
2531+
}
2532+
2533+
func testArg4(_ x: consuming EmptyStruct) {
2534+
_ = x
2535+
}
2536+
2537+
func testArg4a(_ x: consuming EmptyStruct) {
2538+
// expected-error @-1 {{'x' consumed more than once}}
2539+
_ = x // expected-note {{consuming use here}}
2540+
_ = x // expected-note {{consuming use here}}
2541+
}
2542+
2543+
func testArg4b(_ x: consuming EmptyStruct) {
2544+
// expected-error @-1 {{'x' consumed more than once}}
2545+
// expected-error @-2 {{'x' consumed more than once}}
2546+
_ = x // expected-note {{consuming use here}}
2547+
_ = x // expected-note {{consuming use here}}
2548+
// expected-note @-1 {{consuming use here}}
2549+
let _ = x // expected-note {{consuming use here}}
2550+
}
2551+
2552+
func testArg5(_ x: consuming EmptyStruct) {
2553+
let y = x
2554+
_ = y
2555+
}
2556+
2557+
func testArg6(_ x: consuming EmptyStruct) {
2558+
x.doSomething()
2559+
}
2560+
2561+
func testArg7(_ x: consuming EmptyStruct) {
2562+
x.doSomething3()
2563+
}
2564+
2565+
func testArg7a(_ x: consuming EmptyStruct) {
2566+
// expected-error @-1 {{'x' consumed more than once}}
2567+
x.doSomething3() // expected-note {{consuming use here}}
2568+
x.doSomething3() // expected-note {{consuming use here}}
2569+
}
2570+
}
2571+
2572+
////////////////////////////////////
2573+
// Struct Containing Empty Struct //
2574+
////////////////////////////////////
2575+
2576+
// Make sure that we handle a struct that recursively holds an empty struct
2577+
// correctly.
2578+
@_moveOnly
2579+
struct StructContainingEmptyStruct {
2580+
var x: EmptyStruct
2581+
}
2582+
2583+
func borrow(_ x: consuming StructContainingEmptyStruct) {}
2584+
func consume(_ x: consuming StructContainingEmptyStruct) {}
2585+
2586+
func testStructContainingEmptyStruct() {
2587+
func testArg1(_ x: consuming StructContainingEmptyStruct) {
2588+
borrow(x)
2589+
}
2590+
2591+
func testArg2(_ x: consuming StructContainingEmptyStruct) {
2592+
consume(x)
2593+
}
2594+
2595+
func testArg3(_ x: consuming StructContainingEmptyStruct) {
2596+
let _ = x
2597+
}
2598+
2599+
func testArg4(_ x: consuming StructContainingEmptyStruct) {
2600+
_ = x
2601+
}
2602+
2603+
func testArg5(_ x: consuming StructContainingEmptyStruct) {
2604+
let y = x
2605+
_ = y
2606+
}
2607+
2608+
func testArg6(_ x: consuming StructContainingEmptyStruct) {
2609+
x.x.doSomething()
2610+
}
2611+
2612+
func testArg7(_ x: consuming StructContainingEmptyStruct) {
2613+
x.x.doSomething3()
2614+
}
2615+
2616+
func testArg7a(_ x: consuming StructContainingEmptyStruct) {
2617+
// expected-error @-1 {{'x' consumed more than once}}
2618+
x.x.doSomething3() // expected-note {{consuming use here}}
2619+
x.x.doSomething3() // expected-note {{consuming use here}}
2620+
}
2621+
}
2622+
2623+
////////////////////////////////////
2624+
// Struct Containing Empty Struct //
2625+
////////////////////////////////////
2626+
2627+
// Make sure that we handle a struct that recursively holds an empty struct
2628+
// correctly.
2629+
@_moveOnly
2630+
struct StructContainingTwoEmptyStruct {
2631+
var x: EmptyStruct
2632+
var y: EmptyStruct
2633+
}
2634+
2635+
func borrow(_ x: consuming StructContainingTwoEmptyStruct) {}
2636+
func consume(_ x: consuming StructContainingTwoEmptyStruct) {}
2637+
2638+
func testStructContainingTwoEmptyStruct() {
2639+
func testArg1(_ x: consuming StructContainingTwoEmptyStruct) {
2640+
borrow(x)
2641+
}
2642+
2643+
func testArg2(_ x: consuming StructContainingTwoEmptyStruct) {
2644+
consume(x)
2645+
}
2646+
2647+
func testArg3(_ x: consuming StructContainingTwoEmptyStruct) {
2648+
let _ = x
2649+
}
2650+
2651+
func testArg4(_ x: consuming StructContainingTwoEmptyStruct) {
2652+
_ = x
2653+
}
2654+
2655+
func testArg5(_ x: consuming StructContainingTwoEmptyStruct) {
2656+
let y = x
2657+
_ = y
2658+
}
2659+
2660+
func testArg6(_ x: consuming StructContainingTwoEmptyStruct) {
2661+
x.x.doSomething()
2662+
}
2663+
2664+
func testArg7(_ x: consuming StructContainingTwoEmptyStruct) {
2665+
x.x.doSomething3()
2666+
}
2667+
2668+
func testArg8(_ x: consuming StructContainingTwoEmptyStruct) {
2669+
x.y.doSomething3()
2670+
}
2671+
2672+
func testArg9(_ x: consuming StructContainingTwoEmptyStruct) {
2673+
x.x.doSomething3()
2674+
x.y.doSomething3()
2675+
}
2676+
2677+
func testArg10(_ x: consuming StructContainingTwoEmptyStruct) {
2678+
// expected-error @-1 {{'x' consumed more than once}}
2679+
x.x.doSomething3() // expected-note {{consuming use here}}
2680+
x.y.doSomething3()
2681+
x.x.doSomething3() // expected-note {{consuming use here}}
2682+
}
2683+
}
2684+
2685+
//////////////////////////////////
2686+
// Enum Containing Empty Struct //
2687+
//////////////////////////////////
2688+
2689+
@_moveOnly
2690+
enum MyEnum2 {
2691+
case first(EmptyStruct)
2692+
case second(String)
2693+
}
2694+
2695+
@_moveOnly
2696+
enum MyEnum {
2697+
case first(EmptyStruct)
2698+
case second(String)
2699+
case third(MyEnum2)
2700+
}
2701+
2702+
func borrow(_ x: borrowing MyEnum) {}
2703+
2704+
func testMyEnum() {
2705+
func test1(_ x: consuming MyEnum) {
2706+
if case let .first(y) = x {
2707+
_ = y
2708+
}
2709+
}
2710+
2711+
func test1a(_ x: consuming MyEnum) { // expected-error {{'x' consumed more than once}}
2712+
if case let .first(y) = x { // expected-note {{consuming use here}}
2713+
_ = consume x // expected-note {{consuming use here}}
2714+
_ = y
2715+
}
2716+
}
2717+
2718+
func test1b(_ x: consuming MyEnum) { // expected-error {{'x' consumed more than once}}
2719+
if case let .first(y) = x { // expected-note {{consuming use here}}
2720+
_ = y
2721+
}
2722+
_ = consume x // expected-note {{consuming use here}}
2723+
}
2724+
2725+
func test2(_ x: consuming MyEnum) {
2726+
if case let .third(.first(y)) = x {
2727+
_ = y
2728+
}
2729+
}
2730+
2731+
func test2a(_ x: consuming MyEnum) { // expected-error {{'x' consumed more than once}}
2732+
if case let .third(.first(y)) = x { // expected-note {{consuming use here}}
2733+
_ = consume x // expected-note {{consuming use here}}
2734+
_ = y
2735+
}
2736+
}
2737+
2738+
func test2b(_ x: consuming MyEnum) { // expected-error {{'x' consumed more than once}}
2739+
if case let .third(.first(y)) = x { // expected-note {{consuming use here}}
2740+
_ = y
2741+
}
2742+
_ = consume x // expected-note {{consuming use here}}
2743+
}
2744+
2745+
func test2c(_ x: consuming MyEnum) { // expected-error {{'x' used after consume}}
2746+
if case let .third(.first(y)) = x { // expected-note {{consuming use here}}
2747+
_ = y
2748+
}
2749+
borrow(x) // expected-note {{non-consuming use here}}
2750+
}
2751+
2752+
func test3(_ x: consuming MyEnum) {
2753+
switch x {
2754+
case let .first(y):
2755+
_ = y
2756+
break
2757+
default:
2758+
break
2759+
}
2760+
}
2761+
2762+
func test3a(_ x: consuming MyEnum) { // expected-error {{'x' consumed more than once}}
2763+
switch x { // expected-note {{consuming use here}}
2764+
case let .first(y):
2765+
_ = y
2766+
break
2767+
default:
2768+
break
2769+
}
2770+
_ = consume x // expected-note {{consuming use here}}
2771+
}
2772+
2773+
func test4(_ x: consuming MyEnum) {
2774+
switch x {
2775+
case let .third(.first(y)):
2776+
_ = y
2777+
break
2778+
default:
2779+
break
2780+
}
2781+
}
2782+
2783+
func test4a(_ x: consuming MyEnum) { // expected-error {{'x' consumed more than once}}
2784+
switch x { // expected-note {{consuming use here}}
2785+
case let .third(.first(y)):
2786+
_ = y
2787+
break
2788+
default:
2789+
break
2790+
}
2791+
_ = consume x // expected-note {{consuming use here}}
2792+
}
2793+
}

0 commit comments

Comments
 (0)