Are non-escaping closures always boxed?

During a previous discussion about anonymous structs, one of the stated benefits of structs is that they can be stack allocated while closures are boxed. I am curious, is this always the case?

So for instance, if I have code like this:


func foo(_ block: ()->()) {
    block()
}


while true {
    foo {
        // do something
    }
}


It would seem like the compiler should essentially be able to optimize the closure out of existence, and you shouldn't need retain and release here, because the lifetime of the closure will not escape the scope it's declared in. However is this actually the case?

Nonescaping closures, like the one in your example, won't undergo any refcounting. (Remember that closure arguments are nonescaping by default, unless you annotate them @escaping.) The optimizer will still try to eliminate allocations and refcounting for escaping closures when it can, but that becomes unlikely when the closures are aggregated into larger values, stored, and reloaded at an indefinite period in the future. Those are the sorts of applications where having structs that behave like closures could be useful, to be able to allow a context to escape while still guaranteeing that it doesn't incur additional allocation when wrapped up in other values.

4 Likes