From f57278f941fcbaa6af06c7a7fdc69dc2a8a268cc Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 8 Oct 2025 08:19:57 +0200 Subject: [PATCH] SILCombine: be careful about deleting trivially dead `destructure_struct` instructions A dead `destructure_struct` with an owned argument can appear for a non-copyable or non-escapable struct which has only trivial elements. The instruction is not trivially dead because it ends the lifetime of its operand. Fixes an ownership verification error. --- lib/SILOptimizer/Utils/InstOptUtils.cpp | 8 ++++++++ test/SILOptimizer/sil_combine_moveonly.sil | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index eef9a57156949..3a80401d16374 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -179,6 +179,14 @@ bool swift::isInstructionTriviallyDead(SILInstruction *inst) { if (isa(inst)) return false; + // A dead `destructure_struct` with an owned argument can appear for a non-copyable or + // non-escapable struct which has only trivial elements. The instruction is not trivially + // dead because it ends the lifetime of its operand. + if (isa(inst) && + inst->getOperand(0)->getOwnershipKind() == OwnershipKind::Owned) { + return false; + } + // These invalidate enums so "write" memory, but that is not an essential // operation so we can remove these if they are trivially dead. if (isa(inst)) diff --git a/test/SILOptimizer/sil_combine_moveonly.sil b/test/SILOptimizer/sil_combine_moveonly.sil index 5529219bbc1ee..df42158c3c1d5 100644 --- a/test/SILOptimizer/sil_combine_moveonly.sil +++ b/test/SILOptimizer/sil_combine_moveonly.sil @@ -11,6 +11,11 @@ struct S : ~Copyable { deinit {} } +struct S2 : ~Copyable { + @_hasStorage var a: Int { get set } + @_hasStorage var b: Int { get set } +} + struct FileDescriptor: ~Copyable { var fd: Builtin.Int64 @@ -169,3 +174,14 @@ bb0: %13 = tuple () return %13 } + +// CHECK-LABEL: sil [ossa] @dont_remove_destructure_struct_of_non_copyable : +// CHECK: destructure_struct +// CHECK-LABEL: } // end sil function 'dont_remove_destructure_struct_of_non_copyable' +sil [ossa] @dont_remove_destructure_struct_of_non_copyable : $@convention(method) (@owned S2) -> () { +bb0(%0 : @owned $S2): + (%1, %2) = destructure_struct %0 + %r = tuple () + return %r +} +