Merge pull request #85934 from atrick/fix-closure-liveness

Fix InteriorUseWalker: consider partial_apply [on_stack] an escape
This commit is contained in:
Andrew Trick
2025-12-11 15:05:21 -08:00
committed by GitHub
2 changed files with 37 additions and 2 deletions

View File

@@ -626,9 +626,13 @@ extension InteriorUseWalker: OwnershipUseVisitor {
if value.type.isTrivial(in: function) { if value.type.isTrivial(in: function) {
return .continueWalk return .continueWalk
} }
guard value.type.isEscapable(in: function) else { guard value.mayEscape else {
// Non-escapable dependent values can be lifetime-extended by copying, which is not handled by // Non-escapable dependent values can be lifetime-extended by copying, which is not handled by
// InteriorUseWalker. LifetimeDependenceDefUseWalker does this. // InteriorUseWalker. LifetimeDependenceDefUseWalker does this. Alternatively, we could continue to `walkDownUses`
// but later recognize a copy of a `mayEscape` value to be a pointer escape at that point.
//
// This includes partial_apply [on_stack] which can currently be copied. Although a better solution would be to
// make copying an on-stack closure illegal SIL.
return pointerEscapingUse(of: operand) return pointerEscapingUse(of: operand)
} }
if useVisitor(operand) == .abortWalk { if useVisitor(operand) == .abortWalk {

View File

@@ -319,8 +319,11 @@ bb0(%0 : @owned $String):
return %r return %r
} }
// TODO: If InteriorLiveness handles `partial_apply [on_stack]`, then the destroy can be hoisted.
//
// CHECK-LABEL: sil [ossa] @partial_apply : // CHECK-LABEL: sil [ossa] @partial_apply :
// CHECK: destroy_value %1 // CHECK: destroy_value %1
// CHECK-NEXT: debug_step
// CHECK-NEXT: destroy_value %0 // CHECK-NEXT: destroy_value %0
// CHECK: } // end sil function 'partial_apply' // CHECK: } // end sil function 'partial_apply'
sil [ossa] @partial_apply : $@convention(thin) (@owned String) -> () { sil [ossa] @partial_apply : $@convention(thin) (@owned String) -> () {
@@ -445,3 +448,31 @@ bb0:
%r = tuple () %r = tuple ()
return %r return %r
} }
sil @closure1 : $@convention(thin) (@guaranteed C) -> ()
// rdar165850554: InteriorUseDefWalker must consider copies of an on-stack partial apply when computing liveness of the
// borrowed operand.
// CHECK-LABEL: sil private [ossa] @test_closure_copy :
// CHECK: %1 = copy_value %0
// CHECK: debug_step
// CHECK-NEXT: destroy_value %1
// CHECK-LABEL: } // end sil function 'test_closure_copy'
sil private [ossa] @test_closure_copy : $@convention(thin) (@owned C) -> () {
bb0(%0 : @owned $C):
// create a non-lexical value to trigger mandatory destroy hoisting
%1 = copy_value %0
%2 = function_ref @closure1 : $@convention(thin) (@guaranteed C) -> ()
%3 = partial_apply [callee_guaranteed] [on_stack] %2(%1) : $@convention(thin) (@guaranteed C) -> ()
%4 = copy_value %3
// force a pointer-escape
%5 = unchecked_bitwise_cast %4 to $@noescape @callee_guaranteed () -> ()
destroy_value %3
destroy_value %0
destroy_value %4
debug_step
destroy_value %1
%10 = tuple ()
return %10
}