Rust: #[track_caller] on closures?

Created on 4 Jul 2020  路  7Comments  路  Source: rust-lang/rust

One problem I have is that it's not possible to put #[track_caller] on closures, this is really useful when thread_local is involved because then there's a lot of logic in closures that you might want to propagate
ie

thread_local! {static DONE: bool = false;}

#[track_caller]
fn assert_done() {
    DONE.with(
        #[track_caller]
        |b| assert!(b),
    );
}

fn main() {
    assert_done();
}

_Originally posted by @elichai in https://github.com/rust-lang/rust/issues/47809#issuecomment-653777647_

C-feature-request F-track_caller

Most helpful comment

I've opened https://github.com/rust-lang/rust/pull/74492 with a bare minimum change and test included. It's currently failing because MIR argument counts aren't happy. I'll dig in at some point soon, if anyone has suggestions I'm all ears.

All 7 comments

cc @eddyb I cant' recall right now whether this restriction is a requirement of the implementation we chose or whether we just left it as-designed in the RFC. Can you?

If there is no technical reason not to support it, I would think we should. I imagine one challenge is that calls to closures are being dispatched through the Fn trait, but I imagine we resolve that because we know the callee at monomorphization time, unless it's a virtual call in which case we use a shim?

I would expect this to "just work", if the attribute syntax parses.

Although... the example is a bit suspect, it doesn't specify what the expected behavior is, but the call of the closure in the with method is what you'll get as the assert! failure location, if #[track_caller] works correctly on closures.

If you want to propagate all the way to main, that's hard.
You want to defer errors until you can panic outside the closure, so that you're not inside the with call.

We could also try to make a way to override the "caller location" of a call (and pass in an outer one), but the ergonomics of that are worse for this example (where assert!(DONE.with(|b| b)) "just works").

Although... the example is a bit suspect, it doesn't specify what the expected behavior is, but the call of the closure in the with method is what you'll get as the assert! failure location, if #[track_caller] works correctly on closures.

Ha! That's interesting, I didn't think about the fact that I'm not actually the one calling the closure, so my #[track_caller] on assert_done doesn't do anything because there's no #[track_caller] on the with method and whatever its internals are.

I had assumed that, in the example, the with function would also have #[track_caller]. I could see that becoming a common pattern, but yeah, it does rely on that.

I've opened https://github.com/rust-lang/rust/pull/74492 with a bare minimum change and test included. It's currently failing because MIR argument counts aren't happy. I'll dig in at some point soon, if anyone has suggestions I'm all ears.

Was this page helpful?
0 / 5 - 0 ratings