Closure obtained from reflection cannot properly address input parameters

Given this trivial code:

struct Container {
    let closure: (Int) -> Void
}

let container = Container { param in
    print("\(param)")
}

One can assume calling container.closure(42) will print out 42, which is true.

However, if this same closure is retrieved from the container's Mirror:

let mirror = Mirror(reflecting: container)
let closure = mirror.children
    .first(where: { $0.label == "closure" })!
    .value as! ((Int) -> Void)

... then calling closure(42) distorts the parameter's value, and it prints out 6166589480.

The same thing happens if you use String instead of Int, and I assume with other types too. If I pass a reference to an object, expectedly that reference gets messed up as well and I get EXC_BAD_ACCESS when trying to access the object.

Is this a bug in the Standard Library or am I missing something?

2 Likes

Looks like a bug. As a workaround wrapping closure into its own type works for me:

struct Closure {
    let proc: (Int) -> Void
}

struct Container {
    let closure: Closure
}

let container = Container(closure: Closure { param in
    print("\(param)")
})

container.closure.proc(42)
let mirror = Mirror(reflecting: container)
let closure = mirror.children
    .first(where: { $0.label == "closure" })!
    .value as! Closure
closure.proc(42)
print()

Unfortunately, I don't have access to the Container's sources, which is the main reason I had to appeal to reflection in the first place. Submitted a radar.

I see. If you have a good understanding what Container type has you can create your version of it, typecast and call through your copy without the need of using mirroring API:

struct Container {
    let closure: (Int) -> Void
}

var container = Container { param in
    print("\(param)")
}

container.closure(42)

struct ContainerCopy {
    let closure: (Int) -> Void
}

withUnsafeBytes(of: &container) { p in
    let r = p.baseAddress!.assumingMemoryBound(to: ContainerCopy.self).pointee
    r.closure(42)
}
print()

Workarounds aside it would be interesting if someone from standard library / compiler team comment on this bug.

1 Like