I had a surprising issue come up recently that I don’t totally understand, which is possibly related to escaping/non-escaping closures. Consider the following code, which will ‘chain’ together a bunch of methods such that they get called sequentially.
import Foundation
final class Chainer {
var callees: [Callee]
init(callees: [Callee]) {
self.callees = callees
}
func performAndInvoke(_ invocation: () -> Void) {
// if you wrap this implementation in a call to
// `withoutActuallyEscaping(invocation) { invocation in ... }`
// then it won't crash at runtime. why?
var chainedInvocation = invocation
for callee in callees.reversed() {
let nextInvocation = chainedInvocation
chainedInvocation = {
callee.performAndInvoke(nextInvocation)
}
}
chainedInvocation()
}
}
final class Callee {
var value: String
init(value: String) {
self.value = value
}
func performAndInvoke(_ invocation: () -> Void) {
print("performing with value: \(value)")
invocation()
}
}
let c: [Callee] = [
Callee(value: "one"),
// commenting out the second element prevents a runtime crash. why?
Callee(value: "two"),
]
let ch = Chainer(callees: c)
ch.performAndInvoke({ print("final invocation") })
When I compile and run this snippet from the command line, I get a runtime crash with the error terminated by signal SIGBUS (Misaligned address error)
. If I compile with the undefined behavior sanitizer (swiftc -sanitize=undefined ...
) and run it, I get slightly more info, but it’s still not super-clear to me what the underlying problem is:
==22622==ERROR: UndefinedBehaviorSanitizer: BUS on unknown address (pc 0x000199ea9f5c bp 0x000199eea5e4 sp 0x00016b9a1cd0 T16579874)
==22622==The signal is caused by a UNKNOWN memory access.
==22622==Hint: this fault was caused by a dereference of a high value address (see register values below). Disassemble the provided pc to learn which register was used.
If I wrap the implementation within a call to withoutActuallyEscaping
, it seems to fix the issue, but I’m not entirely sure why. It’s a bit surprising to me that this compiles successfully but seems to cause invalid memory access at runtime. Any ideas what the underlying issue is in this instance? Thanks in advance!