Repository navigation
Description
This is a tracking issue for #[track_caller] on async fn.
The feature gate #[feature(async_fn_track_caller)] for the issue is not implemented yet (#110009)
About tracking issues
Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.
Steps
- Update tracking behavior (see Unresolved Questions)
- Adjust documentation (see instructions on rustc-dev-guide)
- Stabilization PR (see instructions on rustc-dev-guide)
Unresolved Questions
(Copied over from: #87417 (comment))
It's not 100% clear that tracking the poller, rather than the original caller, is the ideal behavior. That said, we may still be able to resolve the question without an RFC.
Arguments in favor of tracking the poller (current behavior):
- The user might expect to see the call that's happening now rather than one that happened in the past.
- Prevents us from having to store the caller in the Future type.
- Usually poller and caller are the same function, anyway, so we should do the simpler and more performant thing.
Arguments in favor of tracking the original caller instead of the poller:
- User might expect to see the actual caller; especially given the name track_caller, showing the poller can confusing.
- A &'static Location is only one pointer; storing it is not so bad.
- Combinators won't need to add #[track_caller] to "thread through" the caller information.
- If we tracked the poller, #[track_caller] on a spawned task would show the executor.
- There's no way today for the executor to save off caller location information (e.g. in spawn) and re-provide it to the polled function, even if it wanted to. We could fix this with a set_caller API.
- Tracking won't work for &dyn Future. Executors can easily work around this if they have an API with access to the underlying type before coercing to dyn, as most do (playground). But if you happen to already have a dyn Future, you'll be stuck.
Activity
bryangarza commentedon Apr 6, 2023
FYI @tmandry
bryangarza commentedon Apr 6, 2023
@rustbot label +T-lang
tmandry commentedon Apr 10, 2023
@rust-lang/wg-async discussed in this zulip thread. We were leaning toward tracking the original caller rather than the poller, based on the use case we knew about (assertion helpers). If there are other use cases or points of agreement/disagreement with this, please leave them in the linked zulip thread.
Swatinem commentedon May 31, 2023
Another idea worth revisiting here may be:
In #104833, I also added
#[track_caller]support for async blocks. Pretty much because it was trivial to do so, and actually supporting it separately would have been more work.It might still be worth doing that, specifically to split that into its own
track_async_block_calleror similar feature.CryZe commentedon Aug 26, 2025
It probably makes sense to simply allow the user to choose:
#[track_caller(poll)]or
#[track_caller(original)](+- bikeshedding on the names)
feat(client): add coordinode-client crate with source location tracking
feat(client): add coordinode-client crate with source location tracking
theemathas commentedon Jul 15, 2026
Lang-nominating to request stabilization, as the one unresolved question seems relatively easy to decide on. See discussion at #wg-async > async_fn_track_caller stablization status?
traviscross commentedon Jul 15, 2026
We do our stabilization decisions on PRs and on the basis of the stabilization report and accompanying Reference PR. So the best we could do here, in the meeting, would be to invite a stabilization PR and offer any thoughts we have about things we might want to see there.
18 remaining items
Rollup merge of rust-lang#161972 - theemathas:async-track_caller-test…
Rollup merge of rust-lang#161972 - theemathas:async-track_caller-test…
Rollup merge of rust-lang#161972 - theemathas:async-track_caller-test…
Unrolled build for #161972 in rollup 162229
Rollup merge of #161972 - theemathas:async-track_caller-tests, r=Shoy…
theemathas commentedon Sep 27, 2026
I've discovered the following edge case, and I would like T-lang decision on how this code should behave.
According to the reference,
#[track_caller]on a method in the trait definition also applies to that method in all implementations. (Thanks to an LLM for pointing this out as a gap that my draft implementation didn't support.)The problem is,
#[track_caller]on a function that returns an async block doesn't apply to the async block at all. Meanwhile, as per previous T-lang decision,#[track_caller]on anasync fncauses the async block to track the caller of the function. That is,#[track_caller]on these two things have very different effects. This makes the the desired behavior for "#[track_caller]inheritance" rather unclear.We need to decide what the behavior of the above code snippet is. For each of
Trait1andTrait2, there are four possible behaviors:I think that the behavior that makes the most sense is for
Trait1to have the panic point to the caller of the function, whileTrait2have the panic point to the line where the panic is written. This is a bit surprising, but it makes sense to me with the mental model of "#[track_caller]gets copy-pasted onto the method on every impl block of the trait". I am asking what T-lang thinks is the correct behavior.theemathas commentedon Sep 29, 2026
Another question: does there need to be a new FCW for the case in #163406 (plus some waiting time) before we stabilize this feature?
tmandry commentedon Oct 28, 2026
My inclination is to adopt the following model:
You should be able to write
#[track_caller]on async blocks, async closures, and regular closures. Closures do not propagate caller information from the defining function, and they propagate caller location from the caller only with#[track_caller].#[track_caller] async fn foo() -> T { expr }is equivalent to#[track_caller] fn foo() -> impl Future<Output = T> { #[track_caller] async move { expr } }.#[track_caller] async {}is equivalent to(#[track_caller] async || {})().Is there a reason we can't do this? I proposed it in today's lang meeting and people thought it sounded right to them.
Of course I can see it being useful in some cases to track the poller/awaiter, but that can be a separate knob, and this model seems consistent.
theemathas commentedon Oct 30, 2026
@tmandry If we go with that idea, then this has a side effect where, in the following code, the
Location::caller()call returns the location of the caller offoo(). Is this intended?Also, there's an unanswered question in #110011 (comment)
theemathas commentedon Oct 31, 2026
@tmandry Also, I don't think you've answered my question of how my code snippet with
Trait1andTrait2should behave.theemathas commentedon Nov 8, 2026
I have a writeup at https://hackmd.io/@2zGWDWY_QTaaY-pDimKARg/Hy1QajnpGx, summarizing the status of this, and has questions I have for T-lang.