Description
Previous ID | SR-15803 |
Radar | None |
Original Reporter | Stefan Springer (JIRA User) |
Type | Bug |
Additional Detail from JIRA
Votes | 0 |
Component/s | Compiler |
Labels | Bug |
Assignee | None |
Priority | Medium |
md5: f656f5102814b117be9fbb97205c3891
Issue Description:
When the removal of the only reference to an item triggers the removal of the only reference to another item and so on, we get a stack overflow of the deinits (could be implicit deinits just created by the compiler for reference counting) when this chain is long enough (depending on how "thick" each deinit is, maybe forum entry https://forums.swift.org/t/a-roadmap-for-improving-swift-performance-predictability-arc-improvements-and-ownership-control/54206 would help with that "thickness" of each item at least). See the example code down below.
See https://github.com/hatzel/swift-simple-queue/blob/6d03e672874a33d05aff4607c3d152bd5e4a2584/Sources/FifoQueue.swift#L8 what people are tying to do to circumvent this problem, but I really think this should not occur at all, my gut feeling is that these nested calls of deinits should be unnecessary (maybe think tail recursion as an analogy). There should be a more clever way to manage the reference counting.
Also see the discussion in the Swift forums: https://forums.swift.org/t/deep-recursion-in-deinit-should-not-happen/54987
(Note that although this ticket might formally count as asking for an improvement, I created the ticket as a bug report because I think the issue is quite serious.)
Example code:
class Chained {
var next: Chained? = nil
init() {}
}
print("building...")
var first: Chained? = Chained()
var last = first
//for _ in 1...1_000_000 { // for release mode
for _ in 1...100_000 { // for debug mode
let next = Chained()
last?.next = next
last = next
}
last = nil
print("forgetting references...")
first = nil // Thread 1: EXC_BAD_ACCESS (code=2, address=0x16f603ff0)
print("done")