Skip to content

Commit b524329

Browse files
Merge pull request #16945 from aschwaighofer/fix_closure_lifetime_fixup_of_deinit
ClosureLifetimeFixup: Fix closure insertion point in deallocating dei…
2 parents a22b360 + 968d227 commit b524329

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,27 @@ static SILBasicBlock *getOptionalDiamondSuccessor(SwitchEnumInst *SEI) {
6666
return nullptr;
6767
}
6868

69+
/// Find a safe insertion point for closure destruction. We might create a
70+
/// closure that captures self in deinit of self. In this situation it is not
71+
/// safe to destroy the closure after we called super deinit. We have to place
72+
/// the closure destruction before that call.
73+
///
74+
/// %deinit = objc_super_method %0 : $C, #A.deinit!deallocator.foreign
75+
/// %super = upcast %0 : $C to $A
76+
/// apply %deinit(%super) : $@convention(objc_method) (A) -> ()
77+
/// end_lifetime %super : $A
78+
static SILInstruction *getDeinitSafeClosureDestructionPoint(TermInst *Term) {
79+
for (auto It = Term->getParent()->rbegin(), E = Term->getParent()->rend();
80+
It != E; ++It) {
81+
if (auto *EndLifetime = dyn_cast<EndLifetimeInst>(&*It)) {
82+
auto *SuperInstance = EndLifetime->getOperand()->getDefiningInstruction();
83+
assert(SuperInstance && "Expected an instruction");
84+
return SuperInstance;
85+
}
86+
}
87+
return Term;
88+
}
89+
6990
/// Extend the lifetime of the convert_escape_to_noescape's operand to the end
7091
/// of the function.
7192
static void extendLifetimeToEndOfFunction(SILFunction &Fn,
@@ -111,10 +132,11 @@ static void extendLifetimeToEndOfFunction(SILFunction &Fn,
111132
Fn.findExitingBlocks(ExitingBlocks);
112133
for (auto *Exit : ExitingBlocks) {
113134
auto *Term = Exit->getTerminator();
114-
SILBuilderWithScope B(Term);
115-
B.setInsertionPoint(Term);
135+
auto *SafeClosureDestructionPt = getDeinitSafeClosureDestructionPoint(Term);
136+
SILBuilderWithScope B(SafeClosureDestructionPt);
116137
B.createDestroyAddr(loc, Slot);
117-
B.createDeallocStack(loc, Slot);
138+
SILBuilderWithScope B2(Term);
139+
B2.createDeallocStack(loc, Slot);
118140
}
119141
}
120142

test/SILOptimizer/closure_lifetime_fixup_objc.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,24 @@ public func dontCrash() {
7979
queue.sync { }
8080
}
8181
}
82+
83+
@_silgen_name("getDispatchQueue")
84+
func getDispatchQueue() -> DispatchQueue
85+
86+
// We must not release the closure after calling super.deinit.
87+
// CHECK: sil hidden @$S27closure_lifetime_fixup_objc1CCfD : $@convention(method) (@owned C) -> () {
88+
// CHECK: bb0([[SELF:%.*]] : $C):
89+
// CHECK: [[F:%.*]] = function_ref @$S27closure_lifetime_fixup_objc1CCfdyyXEfU_
90+
// CHECK: [[PA:%.*]] = partial_apply [callee_guaranteed] [[F]](%0)
91+
// CHECK: [[OPT:%.*]] = enum $Optional<@callee_guaranteed () -> ()>, #Optional.some!enumelt.1, [[PA]]
92+
// CHECK: [[DEINIT:%.*]] = objc_super_method [[SELF]] : $C, #NSObject.deinit!deallocator.foreign
93+
// CHECK: release_value [[OPT]] : $Optional<@callee_guaranteed () -> ()>
94+
// CHECK: [[SUPER:%.*]] = upcast [[SELF]] : $C to $NSObject // user: %34
95+
// CHECK-NEXT: apply [[DEINIT]]([[SUPER]]) : $@convention(objc_method) (NSObject) -> ()
96+
// CHECK-NEXT: [[T:%.*]] = tuple ()
97+
// CHECK-NEXT: return [[T]] : $()
98+
class C: NSObject {
99+
deinit {
100+
getDispatchQueue().sync(execute: { _ = self })
101+
}
102+
}

0 commit comments

Comments
 (0)