Skip to content

[5.9] Batched cherry-picks #65195

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
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
5 changes: 5 additions & 0 deletions lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ TypeSubElementCount::TypeSubElementCount(SILType type, SILModule &mod,
numElements += TypeSubElementCount(
type.getFieldType(fieldDecl, mod, context), mod, context);
number = numElements;

// If we do not have any elements, just set our size to 1.
if (numElements == 0)
number = 1;

return;
}

Expand Down
17 changes: 16 additions & 1 deletion lib/SILGen/SILGenConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,22 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
if (!isDelegating) {
auto *typeDC = ctor->getDeclContext();
auto *nominal = typeDC->getSelfNominalTypeDecl();
emitMemberInitializers(ctor, selfDecl, nominal);

// If we have an empty move only struct, then we will not initialize it with
// any member initializers, breaking SIL. So in that case, just construct a
// SIL struct value and initialize the memory with that.
//
// DISCUSSION: This only happens with noncopyable types since the memory
// lifetime checker doesn't seem to process trivial locations. But empty
// move only structs are non-trivial, so we need to handle this here.
if (isa<StructDecl>(nominal) && nominal->isMoveOnly() &&
nominal->getStoredProperties().empty()) {
auto *si = B.createStruct(ctor, lowering.getLoweredType(), {});
B.emitStoreValueOperation(ctor, si, selfLV.getLValueAddress(),
StoreOwnershipQualifier::Init);
} else {
emitMemberInitializers(ctor, selfDecl, nominal);
}
}

emitProfilerIncrement(ctor->getTypecheckedBody());
Expand Down
2 changes: 1 addition & 1 deletion lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1333,7 +1333,7 @@ checkForDestructureThroughDeinit(MarkMustCheckInst *rootAddress, Operand *use,
// cannot contain non-copyable types and that our parent root type must be
// an enum, tuple, or struct.
if (auto *nom = iterType.getNominalOrBoundGenericNominal()) {
if (mod.lookUpMoveOnlyDeinitFunction(nom)) {
if (nom->getValueTypeDestructor()) {
// If we find one, emit an error since we are going to have to extract
// through the deinit. Emit a nice error saying what it is. Since we
// are emitting an error, we do a bit more work and construct the
Expand Down
2 changes: 2 additions & 0 deletions lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -774,4 +774,6 @@ void DiagnosticEmitter::emitCannotDestructureDeinitNominalError(
}
diagnose(astContext, consumingUser,
diag::sil_moveonlychecker_consuming_use_here);
astContext.Diags.diagnose(deinitedNominal->getValueTypeDestructor(),
diag::sil_moveonlychecker_deinit_here);
}
14 changes: 14 additions & 0 deletions test/Interpreter/moveonly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,17 @@ Tests.test("deinit not called in init when assigned") {
}
expectEqual(0, FD2.count)
}

Tests.test("empty struct") {
@_moveOnly
struct EmptyStruct {
func doSomething() {}
var value: Bool { false }
}

let e = EmptyStruct()
e.doSomething()
if e.value {
let _ = consume e
}
}
24 changes: 24 additions & 0 deletions test/SILGen/moveonly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -779,3 +779,27 @@ func checkMarkMustCheckOnCaptured(x: __owned FD) {
func clodger<T>(_: () -> T) {}
clodger({ consumeVal(x) })
}

//////////////////
// Empty Struct //
//////////////////

@_moveOnly
struct EmptyStruct {
// Make sure we explicitly initialize empty struct as appropriate despite the
// fact we do not have any fields.
//
// CHECK-LABEL: sil hidden [ossa] @$s8moveonly11EmptyStructVACycfC : $@convention(method) (@thin EmptyStruct.Type) -> @owned EmptyStruct {
// CHECK: [[BOX:%.*]] = alloc_box ${ var EmptyStruct }, var, name "self"
// CHECK: [[MARKED_UNINIT:%.*]] = mark_uninitialized [rootself] [[BOX]]
// CHECK: [[PROJECT:%.*]] = project_box [[MARKED_UNINIT]]
// CHECK: [[STRUCT:%.*]] = struct $EmptyStruct ()
// CHECK: store [[STRUCT]] to [init] [[PROJECT]]
// CHECK: [[MV_CHECK:%.*]] = mark_must_check [assignable_but_not_consumable] [[PROJECT]]
// CHECK: [[LOADED_VALUE:%.*]] = load [copy] [[MV_CHECK]]
// CHECK: destroy_value [[MARKED_UNINIT]]
// CHECK: return [[LOADED_VALUE]]
// CHECK: } // end sil function '$s8moveonly11EmptyStructVACycfC'
init() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ struct DeinitStruct {
var fifth: MoveOnlyKlass

deinit {}
// expected-note @-1 {{deinit declared here}}
// expected-note @-2 {{deinit declared here}}
// expected-note @-3 {{deinit declared here}}
// expected-note @-4 {{deinit declared here}}
// expected-note @-5 {{deinit declared here}}
// expected-note @-6 {{deinit declared here}}
// expected-note @-7 {{deinit declared here}}
// expected-note @-8 {{deinit declared here}}
// expected-note @-9 {{deinit declared here}}
// expected-note @-10 {{deinit declared here}}
}

func testConsumeCopyable(_ x: consuming DeinitStruct) {
Expand Down
Loading