From 7631728518689747728b5fec9eb6522393f52b84 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Fri, 25 Sep 2026 18:41:16 +0700 Subject: [PATCH 1/9] Test `#[track_caller]` in async on gcc backend Previously, in https://github.com/rust-lang/rust/pull/161972, this test was skipped on gcc, due to a bug in rustc_codegen_gcc. The bug was fixed by the subtree update at https://github.com/rust-lang/rust/pull/162499. --- tests/ui/async-await/track-caller/panic-track-caller.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index 294150500170d..0cb3685398cdc 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -1,13 +1,13 @@ // This test is duplicated (with changes) at // src/tools/miri/tests/pass/async-panic-track-caller.rs -// FIXME: catch_unwind is broken in gcc. Will be fixed in the next rustc_codegen_gcc sync. -//@ ignore-backends: gcc //@ run-pass //@ edition:2021 //@ revisions: afn cls afn_cls nofeat //@ needs-unwind // gate-test-async_fn_track_caller +// +// Padding comment so that the line numbers are the same as async-panic-track-caller.rs #![feature(stmt_expr_attributes)] #![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] #![cfg_attr(any(cls, afn_cls), feature(closure_track_caller))] From 9da79b053bcd7579c5d468fd70f10a72fe3e10ea Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Sun, 4 Oct 2026 15:30:51 +0700 Subject: [PATCH 2/9] consteval: Rework caller location handling Instead of walking up the call stack each time we need the caller location, we emulate the run time behavior of passing an implicit caller location argument in #[track_caller] functions. We record a representation of that argument in each frame. For inlined functions though, we still do a "stack walk" within that one frame. --- .../src/const_eval/machine.rs | 4 +- .../rustc_const_eval/src/interpret/call.rs | 21 +++++- .../src/interpret/eval_context.rs | 66 ++++++++----------- .../src/interpret/intrinsics.rs | 2 +- .../rustc_const_eval/src/interpret/stack.rs | 9 ++- .../rustc_const_eval/src/interpret/util.rs | 2 +- src/tools/miri/src/helpers.rs | 2 + 7 files changed, 58 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 3f7731ce95583..9efe2223c3bb1 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -253,7 +253,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { } let msg = Symbol::intern(self.read_str(&msg_place)?); - let span = self.find_closest_untracked_caller_location(); + let span = self.caller_location(); let (file, line, col) = self.location_triple_for_span(span); return Err(ConstEvalErrKind::Panic { msg, file, line, col }).into(); } else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) { @@ -463,7 +463,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> { let msg = Symbol::intern(msg); - let span = ecx.find_closest_untracked_caller_location(); + let span = ecx.caller_location(); let (file, line, col) = ecx.location_triple_for_span(span); Err(ConstEvalErrKind::Panic { msg, file, line, col }).into() } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index c7615a216312e..f1b551441a675 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -10,7 +10,7 @@ use rustc_attr_ir::find_attr; use rustc_hir::def_id::DefId; use rustc_middle::mir; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; -use rustc_middle::ty::{self, AdtDef, FieldDef, Instance, Ty, VariantDef}; +use rustc_middle::ty::{self, AdtDef, FieldDef, Instance, InstanceKind, Ty, VariantDef}; use rustc_span::{bug, span_bug}; use rustc_target::callconv::{ArgAbi, FnAbi}; use tracing::field::Empty; @@ -456,12 +456,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, M::Provenance>], with_caller_location: bool, + callee_receives_caller_location: bool, destination: &PlaceTy<'tcx, M::Provenance>, mut cont: ReturnContinuation, ) -> InterpResult<'tcx> { let _trace = enter_trace_span!(M, step::init_stack_frame, %instance, tracing_separate_thread = Empty); let def_id = instance.def_id(); + assert!(!with_caller_location || callee_receives_caller_location); + let caller_location = callee_receives_caller_location.then(|| self.caller_location()); + // The first order of business is to figure out the callee signature. // However, that requires the list of variadic arguments. // We use the *caller* information to determine where to split the list of arguments, @@ -542,6 +546,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .collect::>() ); + self.frame_mut().track_caller_arg = caller_location; + // Determine whether there is a special VaList argument. This is always the // last argument, and since arguments start at index 1 that's `arg_count`. let va_list_arg = callee_fn_abi.c_variadic.then(|| mir::Local::from_usize(body.arg_count)); @@ -563,7 +569,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // The "where they come from" part is easy, we expect the caller to do any special handling // that might be required here (e.g. for untupling). // If `with_caller_location` is set we pretend there is an extra argument (that - // we will not pass; our `caller_location` intrinsic implementation walks the stack instead). + // we will not pass normally; our `caller_location` intrinsic implementation handles + // that separately). assert_eq!( args.len() + if with_caller_location { 1 } else { 0 }, caller_fn_abi.args.len(), @@ -847,12 +854,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Cow::from(args) }; + // Figure out if the callee receives a caller location argument. + // In the case where we are calling the fallback body of + // a `#[rustc_intrinsic] #[track_caller] fn`, we pretend that + // the caller doesn't pass a caller location argument (for checking ABI), + // but the caller receives a caller location argument from thin air anyway. + let callee_receives_caller_location = with_caller_location + || (instance.def.requires_caller_location(*self.tcx) + && matches!(instance.def, InstanceKind::Item(def_id) if self.tcx.intrinsic(def_id).is_some())); + self.init_stack_frame( instance, body, caller_fn_abi, &args, with_caller_location, + callee_receives_caller_location, destination, ReturnContinuation::Goto { ret: target, unwind }, ) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index a74b323c395e6..d6d80c3e3506a 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; +use std::convert::identity; use either::{Left, Right}; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; @@ -18,7 +19,7 @@ use rustc_middle::ty::{ use rustc_span::{Span, span_bug}; use rustc_structures::Limit; use rustc_target::callconv::FnAbi; -use tracing::{debug, trace}; +use tracing::trace; use super::{ Frame, FrameInfo, GlobalId, InterpErrorKind, InterpResult, MPlaceTy, Machine, MemPlaceMeta, @@ -376,49 +377,34 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a - /// frame which is not `#[track_caller]`. This matches the `caller_location` intrinsic, + /// Grabs the implicit caller location argument, if there is one, + /// or falls back to the "current" span. This matches the `caller_location` intrinsic, /// and is primarily intended for the panic machinery. - pub(crate) fn find_closest_untracked_caller_location(&self) -> Span { - for frame in self.stack().iter().rev() { - debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); - - // Assert that the frame we look at is actually executing code currently - // (`loc` is `Right` when we are unwinding and the frame does not require cleanup). - let loc = frame.loc.left().unwrap(); - - // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all - // (such as `box`). Use the normal span by default. - let mut source_info = *frame.body.source_info(loc); - - // If this is a `Call` terminator, use the `fn_span` instead. - let block = &frame.body.basic_blocks[loc.block]; - if loc.statement_index == block.statements.len() { - debug!( - "find_closest_untracked_caller_location: got terminator {:?} ({:?})", - block.terminator(), - block.terminator().kind, - ); - if let mir::TerminatorKind::Call { fn_span, .. } = block.terminator().kind { - source_info.span = fn_span; - } - } + pub(crate) fn caller_location(&self) -> Span { + let frame = self.frame(); - let caller_location = if frame.instance.def.requires_caller_location(*self.tcx) { - // We use `Err(())` as indication that we should continue up the call stack since - // this is a `#[track_caller]` function. - Some(Err(())) - } else { - None - }; - if let Ok(span) = - frame.body.caller_location_span(source_info, caller_location, *self.tcx, Ok) - { - return span; - } + // Assert that the frame we look at is actually executing code currently + // (`loc` is `Right` when we are unwinding and the frame does not require cleanup). + let loc = frame.loc.left().unwrap(); + + assert_eq!( + frame.track_caller_arg.is_some(), + frame.instance.def.requires_caller_location(*self.tcx) + ); + + // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all + // (such as `box`). Use the normal span by default. + let mut source_info = *frame.body.source_info(loc); + + // If this is a `Call` terminator, use the `fn_span` instead. + let block = &frame.body.basic_blocks[loc.block]; + if loc.statement_index == block.statements.len() + && let mir::TerminatorKind::Call { fn_span, .. } = block.terminator().kind + { + source_info.span = fn_span; } - span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found") + frame.body.caller_location_span(source_info, frame.track_caller_arg, *self.tcx, identity) } /// Returns the actual dynamic size and alignment of the place at the given type. diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index b8b372ad1dab1..d66f4cf900eca 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -293,7 +293,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } sym::caller_location => { - let span = self.find_closest_untracked_caller_location(); + let span = self.caller_location(); let val = self.tcx.span_as_caller_location(span); let val = self.const_val_to_op(val, self.tcx.caller_location_ty(), Some(dest.layout))?; diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 8a731e25d8f4b..6d5c7a78d9f73 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -96,6 +96,9 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { /// frame is popped. pub(super) va_list: Vec>, + /// The implicit caller location argument that is passed by this function's caller, if any. + pub(super) track_caller_arg: Option, + /// The span of the `tracing` crate is stored here. /// When the guard is dropped, the span is exited. This gives us /// a full stack trace on all tracing statements. @@ -255,13 +258,14 @@ impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> { Frame { body: self.body, instance: self.instance, + extra, return_cont: self.return_cont, return_place: self.return_place, locals: self.locals, va_list: self.va_list, - loc: self.loc, - extra, + track_caller_arg: self.track_caller_arg, tracing_span: self.tracing_span, + loc: self.loc, } } } @@ -397,6 +401,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return_place: return_place.clone(), locals, va_list: vec![], + track_caller_arg: None, instance, tracing_span: SpanGuard::new(), extra: (), diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 4efef90581080..416207371e51c 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -25,7 +25,7 @@ pub(crate) fn type_implements_dyn_trait<'tcx, M: Machine<'tcx>>( let ty::Dynamic(preds, _) = trait_ty.kind() else { span_bug!( - ecx.find_closest_untracked_caller_location(), + ecx.cur_span(), "Invalid type provided to type_implements_predicates. U must be dyn Trait, got {trait_ty}." ); }; diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index b0ff200c0614b..fbbf99c7f3d87 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -429,6 +429,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; + let callee_receives_caller_location = f.def.requires_caller_location(*this.tcx); // This will also show proper errors if there is any ABI mismatch. this.init_stack_frame( f, @@ -436,6 +437,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { caller_fn_abi, &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::>(), /*with_caller_location*/ false, + callee_receives_caller_location, &dest.into(), cont, ) From b1f4ad192f635d37b01192eb790595daaee79872 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Sun, 27 Sep 2026 22:09:19 +0700 Subject: [PATCH 3/9] Improve tests for async track caller --- .../tests/pass/async-panic-track-caller.rs | 262 ++++++++++++++-- .../async-closure-gate.afn.stderr | 34 +- .../async-closure-gate.cls.stderr | 4 +- .../async-closure-gate.nofeat.stderr | 34 +- .../track-caller/async-closure-gate.rs | 26 +- .../panic-track-caller.cls.stderr | 50 ++- .../panic-track-caller.cls_opt.stderr | 88 ++++++ .../panic-track-caller.nofeat.stderr | 50 ++- .../panic-track-caller.nofeat_opt.stderr | 88 ++++++ .../track-caller/panic-track-caller.rs | 290 +++++++++++++++--- 10 files changed, 840 insertions(+), 86 deletions(-) create mode 100644 tests/ui/async-await/track-caller/panic-track-caller.cls_opt.stderr create mode 100644 tests/ui/async-await/track-caller/panic-track-caller.nofeat_opt.stderr diff --git a/src/tools/miri/tests/pass/async-panic-track-caller.rs b/src/tools/miri/tests/pass/async-panic-track-caller.rs index 062e07cffc8b9..03685d57c3b74 100644 --- a/src/tools/miri/tests/pass/async-panic-track-caller.rs +++ b/src/tools/miri/tests/pass/async-panic-track-caller.rs @@ -1,20 +1,26 @@ // This test is duplicated (with changes) at // tests/ui/async-await/track-caller/panic-track-caller.rs -//@ edition:2021 -//@ revisions: afn cls afn_cls nofeat -//@ run-native +//@ edition:2024 +//@ revisions: nofeat afn cls afn_cls +//@ compile-flags: -Zinline-mir-hint-threshold=1000 +// +// // // // // Padding comment so that the line numbers are the same as panic-track-caller.rs -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, coroutines, coroutine_trait, gen_blocks)] #![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] #![cfg_attr(any(cls, afn_cls), feature(closure_track_caller))] #![allow(unused)] use std::future::Future; -use std::panic; +use std::ops::Coroutine; +use std::panic::{self, Location}; +use std::pin::pin; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering::Relaxed; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll, Wake}; use std::thread::{self, Thread}; @@ -47,7 +53,11 @@ fn block_on(fut: impl Future) -> T { } } +static LINE: AtomicU32 = AtomicU32::new(0); + async fn bar() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] panic!() } @@ -59,7 +69,9 @@ async fn foo() { #[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] #[track_caller] async fn bar_track_caller() { - panic!() + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); } async fn foo_track_caller() { @@ -73,6 +85,8 @@ impl Foo { #[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] #[track_caller] async fn bar_assoc() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] panic!(); } } @@ -88,6 +102,8 @@ async fn foo_assoc() { async fn foo_closure() { let closure = #[track_caller] async || { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] panic!(); }; let future = closure(); @@ -100,6 +116,8 @@ async fn foo_closure() { async fn foo_block() { let future = #[track_caller] async { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] panic!(); }; future.await; @@ -108,6 +126,8 @@ async fn foo_block() { #[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] #[track_caller] async fn bar_manual_poll() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] panic!(); } @@ -119,7 +139,128 @@ fn foo_manual_poll() { assert_eq!(res, std::task::Poll::Ready(())); } -fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { +trait Trait { + async fn bar_trait_attr_nowhere(); + #[track_caller] + async fn bar_trait_attr_in_trait(); + async fn bar_trait_attr_in_impl(); + #[track_caller] + async fn bar_trait_attr_in_both(); + + #[track_caller] + fn bar_rpit_in_trait() -> impl Future; + #[track_caller] + async fn bar_rpit_in_impl(); +} +impl Trait for Foo { + async fn bar_trait_attr_nowhere() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + async fn bar_trait_attr_in_trait() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + #[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] + #[track_caller] + async fn bar_trait_attr_in_impl() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + #[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] + #[track_caller] + async fn bar_trait_attr_in_both() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + + async fn bar_rpit_in_trait() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + fn bar_rpit_in_impl() -> impl Future { + async { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + } +} + +async fn foo_trait_attr_nowhere() { + let future = Foo::bar_trait_attr_nowhere(); + future.await; +} +async fn foo_trait_attr_in_trait() { + let future = Foo::bar_trait_attr_in_trait(); + future.await; +} +async fn foo_trait_attr_in_impl() { + let future = Foo::bar_trait_attr_in_impl(); + future.await; +} +async fn foo_trait_attr_in_both() { + let future = Foo::bar_trait_attr_in_both(); + future.await; +} + +async fn foo_rpit_in_trait() { + let future = Foo::bar_rpit_in_trait(); + future.await; +} +async fn foo_rpit_in_impl() { + let future = Foo::bar_rpit_in_impl(); + future.await; +} + +#[track_caller] +gen fn bar_gen_fn() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); +} + +fn foo_gen_fn() { + let mut iter = bar_gen_fn(); + let _ = iter.next(); +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls))] +fn foo_gen_block() { + let mut iter = #[track_caller] + gen { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + yield (); + }; + let _ = iter.next(); +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls))] +fn foo_coroutine() { + let coro = #[track_caller] + #[coroutine] + || { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + yield (); + }; + let coro = std::pin::pin!(coro); + let _ = coro.resume(()); +} + +fn assert_panicked_at(f: impl FnOnce() + panic::UnwindSafe, line: u32) { let loc = Arc::new(Mutex::new(None)); let hook = panic::take_hook(); @@ -129,42 +270,121 @@ fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { *loc.lock().unwrap() = info.location().map(|loc| loc.line()) })); } - panic::catch_unwind(f).unwrap_err(); + let result = panic::catch_unwind(f); panic::set_hook(hook); - let x = loc.lock().unwrap().unwrap(); - x + #[cfg(panic = "unwind")] + { + assert!(result.is_err()); + assert_eq!(loc.lock().unwrap().unwrap(), line); + } + #[cfg(not(panic = "unwind"))] + assert!(result.is_ok()); } // FIXME(async_fn_track_caller): Currently, #[track_caller] on an async function // uses the location where the future is awaited or polled. // The correct behavior as per T-lang is to use the location where the function is called. fn main() { - assert_eq!(panicked_at(|| block_on(foo())), 51); + assert_panicked_at(|| block_on(foo()), 61); + assert_eq!(LINE.load(Relaxed), 59); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 67); + assert_panicked_at(|| block_on(foo_track_caller()), 79); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 79); + #[cfg(any(cls, nofeat))] + assert_panicked_at(|| block_on(foo_track_caller()), 74); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 62); + assert_eq!(LINE.load(Relaxed), 72); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 82); + assert_panicked_at(|| block_on(foo_assoc()), 96); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 96); + #[cfg(any(cls, nofeat))] + assert_panicked_at(|| block_on(foo_assoc()), 90); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 76); + assert_eq!(LINE.load(Relaxed), 88); + // FIXME(closure_track_caller): Currently, #[track_caller] on an async closure + // uses the location where the future is awaited or polled. + // It should be changed to use the location where the closure is called, + // so the behavior matches that of `async fn`. + #[cfg(afn_cls)] + assert_panicked_at(|| block_on(foo_closure()), 110); + #[cfg(afn_cls)] + assert_eq!(LINE.load(Relaxed), 110); // FIXME(closure_track_caller): if closure_track_caller is enabled, but // async_fn_track_caller is disabled, then #[track_caller] on async closures // silently do nothing. Either it should function, or we should emit a warning. // See #161961 #[cfg(cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 91); - #[cfg(afn_cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 94); + assert_panicked_at(|| block_on(foo_closure()), 107); + #[cfg(cls)] + assert_eq!(LINE.load(Relaxed), 105); #[cfg(any(cls, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_block())), 105); + assert_panicked_at(|| block_on(foo_block()), 123); + #[cfg(any(cls, afn_cls))] + assert_eq!(LINE.load(Relaxed), 123); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| foo_manual_poll()), 118); + assert_panicked_at(|| foo_manual_poll(), 138); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 138); + #[cfg(any(cls, nofeat))] + assert_panicked_at(|| foo_manual_poll(), 131); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| foo_manual_poll()), 111); + assert_eq!(LINE.load(Relaxed), 129); + + assert_panicked_at(|| block_on(foo_trait_attr_nowhere()), 159); + assert_eq!(LINE.load(Relaxed), 157); + + // FIXME(async_fn_track_caller): This case just currently doesn't work. + assert_panicked_at(|| block_on(foo_trait_attr_in_trait()), 164); + assert_eq!(LINE.load(Relaxed), 162); + + #[cfg(any(afn, afn_cls))] + assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 205); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 205); + #[cfg(any(cls, nofeat))] + assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 171); + #[cfg(any(cls, nofeat))] + assert_eq!(LINE.load(Relaxed), 169); + + #[cfg(any(afn, afn_cls))] + assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 209); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 209); + #[cfg(any(cls, nofeat))] + assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 178); + #[cfg(any(cls, nofeat))] + assert_eq!(LINE.load(Relaxed), 176); + + // FIXME(async_fn_track_caller): This case just currently doesn't work. + assert_panicked_at(|| block_on(foo_rpit_in_trait()), 184); + assert_eq!(LINE.load(Relaxed), 182); + + assert_panicked_at(|| block_on(foo_rpit_in_impl()), 190); + assert_eq!(LINE.load(Relaxed), 188); + + #[cfg(any(afn, afn_cls))] + assert_panicked_at(|| foo_gen_fn(), 230); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 230); + #[cfg(any(cls, nofeat))] + assert_panicked_at(|| foo_gen_fn(), 225); + #[cfg(any(cls, nofeat))] + assert_eq!(LINE.load(Relaxed), 223); + + #[cfg(any(cls, afn_cls))] + assert_panicked_at(|| foo_gen_block(), 244); + #[cfg(any(cls, afn_cls))] + assert_eq!(LINE.load(Relaxed), 244); + + #[cfg(any(cls, afn_cls))] + assert_panicked_at(|| foo_coroutine(), 260); + #[cfg(any(cls, afn_cls))] + assert_eq!(LINE.load(Relaxed), 260); } diff --git a/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr index fb774290bde4e..1f913f0426235 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr @@ -1,5 +1,5 @@ error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:11:13 + --> $DIR/async-closure-gate.rs:12:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:19:13 + --> $DIR/async-closure-gate.rs:20:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:25:13 + --> $DIR/async-closure-gate.rs:26:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:32:17 + --> $DIR/async-closure-gate.rs:33:17 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:40:9 + --> $DIR/async-closure-gate.rs:41:9 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:49:13 + --> $DIR/async-closure-gate.rs:50:13 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -58,6 +58,26 @@ LL | #[track_caller] = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 6 previous errors +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:62:13 + | +LL | let _ = #[track_caller] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:70:13 + | +LL | let _ = #[track_caller] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.cls.stderr b/tests/ui/async-await/track-caller/async-closure-gate.cls.stderr index 5fe7a2fb7d36a..cf7af1c34da02 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.cls.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.cls.stderr @@ -1,5 +1,5 @@ error: `#[track_caller]` on async functions is a no-op - --> $DIR/async-closure-gate.rs:16:1 + --> $DIR/async-closure-gate.rs:17:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | | } = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date note: the lint level is defined here - --> $DIR/async-closure-gate.rs:6:9 + --> $DIR/async-closure-gate.rs:7:9 | LL | #![deny(ungated_async_fn_track_caller)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr index fb774290bde4e..1f913f0426235 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr @@ -1,5 +1,5 @@ error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:11:13 + --> $DIR/async-closure-gate.rs:12:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:19:13 + --> $DIR/async-closure-gate.rs:20:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:25:13 + --> $DIR/async-closure-gate.rs:26:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:32:17 + --> $DIR/async-closure-gate.rs:33:17 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:40:9 + --> $DIR/async-closure-gate.rs:41:9 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:49:13 + --> $DIR/async-closure-gate.rs:50:13 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -58,6 +58,26 @@ LL | #[track_caller] = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 6 previous errors +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:62:13 + | +LL | let _ = #[track_caller] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:70:13 + | +LL | let _ = #[track_caller] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.rs b/tests/ui/async-await/track-caller/async-closure-gate.rs index 13d3a5787ca9d..a35c38249ef2d 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.rs +++ b/tests/ui/async-await/track-caller/async-closure-gate.rs @@ -1,8 +1,9 @@ -//@ edition:2021 +//@ edition:2024 //@ revisions: afn cls afn_cls nofeat //@[afn_cls] check-pass -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, coroutines, gen_blocks)] +#![allow(incomplete_features)] #![deny(ungated_async_fn_track_caller)] #![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] #![cfg_attr(any(cls, afn_cls), feature(closure_track_caller))] @@ -52,3 +53,24 @@ fn foo5() { }; }; } + +// FIXME(gen_blocks): #[track_caller] is apparently not properly linted here? +#[track_caller] +gen fn foo6() {} + +fn foo7() { + let _ = #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + gen { + yield (); + }; +} + +fn foo8() { + let _ = #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + #[coroutine] + || { + yield (); + }; +} diff --git a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr index 5611e53f50a40..78241e7f1dd76 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr @@ -1,11 +1,13 @@ warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:59:1 + --> $DIR/panic-track-caller.rs:69:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_track_caller() { -LL | | panic!() +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); LL | | } | |_- this function will not propagate the caller location | @@ -15,12 +17,14 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:73:5 + --> $DIR/panic-track-caller.rs:85:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] LL | | panic!(); LL | | } | |_____- this function will not propagate the caller location @@ -30,12 +34,14 @@ LL | | } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:108:1 + --> $DIR/panic-track-caller.rs:126:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] LL | | panic!(); LL | | } | |_- this function will not propagate the caller location @@ -44,5 +50,39 @@ LL | | } = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: 3 warnings emitted +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:166:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:173:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted diff --git a/tests/ui/async-await/track-caller/panic-track-caller.cls_opt.stderr b/tests/ui/async-await/track-caller/panic-track-caller.cls_opt.stderr new file mode 100644 index 0000000000000..78241e7f1dd76 --- /dev/null +++ b/tests/ui/async-await/track-caller/panic-track-caller.cls_opt.stderr @@ -0,0 +1,88 @@ +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:69:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_track_caller() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(ungated_async_fn_track_caller)]` on by default + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:85:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:126:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:166:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:173:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted + diff --git a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr index 5611e53f50a40..78241e7f1dd76 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr @@ -1,11 +1,13 @@ warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:59:1 + --> $DIR/panic-track-caller.rs:69:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_track_caller() { -LL | | panic!() +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); LL | | } | |_- this function will not propagate the caller location | @@ -15,12 +17,14 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:73:5 + --> $DIR/panic-track-caller.rs:85:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] LL | | panic!(); LL | | } | |_____- this function will not propagate the caller location @@ -30,12 +34,14 @@ LL | | } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:108:1 + --> $DIR/panic-track-caller.rs:126:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] LL | | panic!(); LL | | } | |_- this function will not propagate the caller location @@ -44,5 +50,39 @@ LL | | } = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: 3 warnings emitted +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:166:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:173:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted diff --git a/tests/ui/async-await/track-caller/panic-track-caller.nofeat_opt.stderr b/tests/ui/async-await/track-caller/panic-track-caller.nofeat_opt.stderr new file mode 100644 index 0000000000000..78241e7f1dd76 --- /dev/null +++ b/tests/ui/async-await/track-caller/panic-track-caller.nofeat_opt.stderr @@ -0,0 +1,88 @@ +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:69:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_track_caller() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(ungated_async_fn_track_caller)]` on by default + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:85:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:126:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:166:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:173:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | #[cfg(panic = "unwind")] +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted + diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index 0cb3685398cdc..f0f9ed223af64 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -2,19 +2,25 @@ // src/tools/miri/tests/pass/async-panic-track-caller.rs //@ run-pass -//@ edition:2021 -//@ revisions: afn cls afn_cls nofeat -//@ needs-unwind +//@ edition:2024 +//@ revisions: nofeat afn cls afn_cls nofeat_opt afn_opt cls_opt afn_cls_opt +//@[nofeat_opt] compile-flags: -O -Zinline-mir-hint-threshold=1000 +//@[afn_opt] compile-flags: -O -Zinline-mir-hint-threshold=1000 +//@[cls_opt] compile-flags: -O -Zinline-mir-hint-threshold=1000 +//@[afn_cls_opt] compile-flags: -O -Zinline-mir-hint-threshold=1000 // gate-test-async_fn_track_caller // -// Padding comment so that the line numbers are the same as async-panic-track-caller.rs -#![feature(stmt_expr_attributes)] -#![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] -#![cfg_attr(any(cls, afn_cls), feature(closure_track_caller))] +#![feature(stmt_expr_attributes, coroutines, coroutine_trait, gen_blocks)] +#![cfg_attr(any(afn, afn_cls, afn_opt, afn_cls_opt), feature(async_fn_track_caller))] +#![cfg_attr(any(cls, afn_cls, cls_opt, afn_cls_opt), feature(closure_track_caller))] #![allow(unused)] use std::future::Future; -use std::panic; +use std::ops::Coroutine; +use std::panic::{self, Location}; +use std::pin::pin; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering::Relaxed; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll, Wake}; use std::thread::{self, Thread}; @@ -47,8 +53,12 @@ fn block_on(fut: impl Future) -> T { } } +static LINE: AtomicU32 = AtomicU32::new(0); + async fn bar() { - panic!() + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); } async fn foo() { @@ -57,9 +67,11 @@ async fn foo() { } #[track_caller] -//[cls,nofeat]~^ WARN `#[track_caller]` on async functions is a no-op +//[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op async fn bar_track_caller() { - panic!() + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); } async fn foo_track_caller() { @@ -71,8 +83,10 @@ struct Foo; impl Foo { #[track_caller] - //[cls,nofeat]~^ WARN `#[track_caller]` on async functions is a no-op + //[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op async fn bar_assoc() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] panic!(); } } @@ -84,10 +98,12 @@ async fn foo_assoc() { // Since compilation is expected to fail for this fn when `closure_track_caller` // is disabled, we test that separately in `async-closure-gate.rs` -#[cfg(any(cls, afn_cls))] +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] async fn foo_closure() { let closure = #[track_caller] async || { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] panic!(); }; let future = closure(); @@ -96,18 +112,22 @@ async fn foo_closure() { // Since compilation is expected to fail for this fn when `closure_track_caller` // is disabled, we test that separately in `async-closure-gate.rs` -#[cfg(any(cls, afn_cls))] +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] async fn foo_block() { let future = #[track_caller] async { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] panic!(); }; future.await; } #[track_caller] -//[cls,nofeat]~^ WARN `#[track_caller]` on async functions is a no-op +//[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op async fn bar_manual_poll() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] panic!(); } @@ -119,7 +139,128 @@ fn foo_manual_poll() { assert_eq!(res, std::task::Poll::Ready(())); } -fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { +trait Trait { + async fn bar_trait_attr_nowhere(); + #[track_caller] + async fn bar_trait_attr_in_trait(); + async fn bar_trait_attr_in_impl(); + #[track_caller] + async fn bar_trait_attr_in_both(); + + #[track_caller] + fn bar_rpit_in_trait() -> impl Future; + #[track_caller] + async fn bar_rpit_in_impl(); +} +impl Trait for Foo { + async fn bar_trait_attr_nowhere() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + async fn bar_trait_attr_in_trait() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + #[track_caller] + //[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op + async fn bar_trait_attr_in_impl() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + #[track_caller] + //[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op + async fn bar_trait_attr_in_both() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + + async fn bar_rpit_in_trait() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + fn bar_rpit_in_impl() -> impl Future { + async { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + } + } +} + +async fn foo_trait_attr_nowhere() { + let future = Foo::bar_trait_attr_nowhere(); + future.await; +} +async fn foo_trait_attr_in_trait() { + let future = Foo::bar_trait_attr_in_trait(); + future.await; +} +async fn foo_trait_attr_in_impl() { + let future = Foo::bar_trait_attr_in_impl(); + future.await; +} +async fn foo_trait_attr_in_both() { + let future = Foo::bar_trait_attr_in_both(); + future.await; +} + +async fn foo_rpit_in_trait() { + let future = Foo::bar_rpit_in_trait(); + future.await; +} +async fn foo_rpit_in_impl() { + let future = Foo::bar_rpit_in_impl(); + future.await; +} + +#[track_caller] +gen fn bar_gen_fn() { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); +} + +fn foo_gen_fn() { + let mut iter = bar_gen_fn(); + let _ = iter.next(); +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] +fn foo_gen_block() { + let mut iter = #[track_caller] + gen { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + yield (); + }; + let _ = iter.next(); +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] +fn foo_coroutine() { + let coro = #[track_caller] + #[coroutine] + || { + LINE.store(Location::caller().line(), Relaxed); + #[cfg(panic = "unwind")] + panic!(); + yield (); + }; + let coro = std::pin::pin!(coro); + let _ = coro.resume(()); +} + +fn assert_panicked_at(f: impl FnOnce() + panic::UnwindSafe, line: u32) { let loc = Arc::new(Mutex::new(None)); let hook = panic::take_hook(); @@ -129,42 +270,117 @@ fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { *loc.lock().unwrap() = info.location().map(|loc| loc.line()) })); } - panic::catch_unwind(f).unwrap_err(); + let result = panic::catch_unwind(f); panic::set_hook(hook); - let x = loc.lock().unwrap().unwrap(); - x + #[cfg(panic = "unwind")] + { + assert!(result.is_err()); + assert_eq!(loc.lock().unwrap().unwrap(), line); + } + #[cfg(not(panic = "unwind"))] + assert!(result.is_ok()); } // FIXME(async_fn_track_caller): Currently, #[track_caller] on an async function // uses the location where the future is awaited or polled. // The correct behavior as per T-lang is to use the location where the function is called. fn main() { - assert_eq!(panicked_at(|| block_on(foo())), 51); + assert_panicked_at(|| block_on(foo()), 61); + assert_eq!(LINE.load(Relaxed), 59); - #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 67); - #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 62); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_panicked_at(|| block_on(foo_track_caller()), 79); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 79); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_panicked_at(|| block_on(foo_track_caller()), 74); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 72); - #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 82); - #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 76); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_panicked_at(|| block_on(foo_assoc()), 96); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 96); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_panicked_at(|| block_on(foo_assoc()), 90); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 88); + #[cfg(any(afn_cls, afn_cls_opt))] + assert_panicked_at(|| block_on(foo_closure()), 110); + #[cfg(any(afn_cls, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 110); // FIXME(closure_track_caller): if closure_track_caller is enabled, but // async_fn_track_caller is disabled, then #[track_caller] on async closures // silently do nothing. Either it should function, or we should emit a warning. // See #161961 - #[cfg(cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 91); - #[cfg(afn_cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 94); + #[cfg(any(cls, cls_opt))] + assert_panicked_at(|| block_on(foo_closure()), 107); + #[cfg(any(cls, cls_opt))] + assert_eq!(LINE.load(Relaxed), 105); #[cfg(any(cls, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_block())), 105); + assert_panicked_at(|| block_on(foo_block()), 123); + #[cfg(any(cls, afn_cls))] + assert_eq!(LINE.load(Relaxed), 123); + + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_panicked_at(|| foo_manual_poll(), 138); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 138); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_panicked_at(|| foo_manual_poll(), 131); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 129); + + assert_panicked_at(|| block_on(foo_trait_attr_nowhere()), 159); + assert_eq!(LINE.load(Relaxed), 157); + + // FIXME(async_fn_track_caller): This case just currently doesn't work. + assert_panicked_at(|| block_on(foo_trait_attr_in_trait()), 164); + assert_eq!(LINE.load(Relaxed), 162); + + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 205); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 205); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 171); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 169); + + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 209); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 209); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 178); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 176); + + // FIXME(async_fn_track_caller): This case just currently doesn't work. + assert_panicked_at(|| block_on(foo_rpit_in_trait()), 184); + assert_eq!(LINE.load(Relaxed), 182); + + assert_panicked_at(|| block_on(foo_rpit_in_impl()), 190); + assert_eq!(LINE.load(Relaxed), 188); + + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_panicked_at(|| foo_gen_fn(), 230); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 230); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_panicked_at(|| foo_gen_fn(), 225); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 223); + + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_panicked_at(|| foo_gen_block(), 244); + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 244); - #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| foo_manual_poll()), 118); - #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| foo_manual_poll()), 111); + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_panicked_at(|| foo_coroutine(), 260); + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 260); } From 6cdbc6aad7686eaa33c27bd37a5246539cc1cce2 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Thu, 1 Oct 2026 21:32:38 +0700 Subject: [PATCH 4/9] Make `#[track_caller] async fn` track the caller, not the poller/awaiter. Part 1: AST lowering --- compiler/rustc_ast_lowering/src/expr.rs | 41 ++++++++++ .../rustc_ast_lowering/src/expr/closure.rs | 6 ++ compiler/rustc_ast_lowering/src/item.rs | 81 +++++++++++++++---- compiler/rustc_attr_ir/src/lang_items.rs | 4 + compiler/rustc_hir/src/hir.rs | 6 ++ compiler/rustc_hir/src/intravisit.rs | 1 + compiler/rustc_hir_pretty/src/lib.rs | 1 + compiler/rustc_span/src/hygiene.rs | 6 ++ compiler/rustc_span/src/symbol.rs | 2 + library/core/src/intrinsics/mod.rs | 1 + .../tests/pass/async-panic-track-caller.rs | 27 +++---- .../track-caller/panic-track-caller.rs | 27 +++---- 12 files changed, 159 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f4bfec24e3b1b..08243203d1b19 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use rustc_ast::node_id::NodeMap; use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::*; +use rustc_attr_ir::find_attr; use rustc_attr_ir::lang_items::LangItem; use rustc_attr_ir::target::Target; use rustc_errors::msg; @@ -411,6 +412,7 @@ impl<'hir> LoweringContext<'_, 'hir> { |this| { this.with_new_scopes(e.span, |this| this.lower_block_expr(block)) }, + None, ) }); let Some(move_expr_state) = move_expr_state else { @@ -866,6 +868,7 @@ impl<'hir> LoweringContext<'_, 'hir> { desugaring_kind: hir::CoroutineDesugaring, coroutine_source: hir::CoroutineSource, body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, + captured_caller_location: Option, ) -> hir::ExprKind<'hir> { let closure_def_id = self.local_def_id(closure_node_id); let coroutine_kind = hir::CoroutineKind::Desugared(desugaring_kind, coroutine_source); @@ -957,11 +960,49 @@ impl<'hir> LoweringContext<'_, 'hir> { kind: hir::ClosureKind::Coroutine(coroutine_kind), constness: hir::Constness::NotConst, explicit_captures, + captured_caller_location, })) } + /// Checks whether `#[track_caller]` annotation on a coroutine function + /// or coroutine closure exists, and should affect the generated coroutine inside. + /// Currently used only for coroutine fns, not coroutine closures. + /// + /// FIXME(closure_track_caller): Change coroutine closures to use this. + pub(super) fn should_track_caller_in_coroutine( + &self, + hir_id: HirId, + node_id: NodeId, + span: Span, + is_in_trait_impl: bool, + ) -> bool { + if !self.tcx.features().async_fn_track_caller() { + return false; + } + if let Some(attrs) = self.curr_owner.attrs.get(&hir_id.local_id) + && find_attr!(*attrs, TrackCaller(_)) + { + // The coroutine function itself is annotated with #[track_caller] + return true; + } + if is_in_trait_impl { + // Check if we need to "inherit" #[track_caller] from the trait definition. + let Some(trait_item_def_id) = + self.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id()) + else { + self.dcx().span_delayed_bug(span, "could not resolve trait item being implemented"); + return false; + }; + return find_attr!(self.tcx, trait_item_def_id, TrackCaller(_)); + } + false + } + /// Forwards a possible `#[track_caller]` annotation from `outer_hir_id` to /// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled. + /// Currently only used for coroutine closures, not coroutine fns. + /// + /// FIXME(closure_track_caller): Remove this function. pub(super) fn maybe_forward_track_caller(&mut self, outer_hir_id: HirId, inner_hir_id: HirId) { if self.tcx.features().async_fn_track_caller() && let Some(attrs) = self.curr_owner.attrs.get(&outer_hir_id.local_id) diff --git a/compiler/rustc_ast_lowering/src/expr/closure.rs b/compiler/rustc_ast_lowering/src/expr/closure.rs index c72956545b7c8..c9a08505823c3 100644 --- a/compiler/rustc_ast_lowering/src/expr/closure.rs +++ b/compiler/rustc_ast_lowering/src/expr/closure.rs @@ -219,6 +219,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind: closure_kind, constness: self.lower_constness(attrs, constness), explicit_captures, + captured_caller_location: None, }); (hir::ExprKind::Closure(c), move_expr_state) @@ -315,8 +316,12 @@ impl<'hir> LoweringContext<'_, 'hir> { body.span, coroutine_marker, hir::CoroutineSource::Closure, + false, ); + // FIXME(closure_track_caller): Currently, coroutine closures, + // unlike coroutine fns, have #[track_caller] track the poller instead of + // the caller of the closure. this.maybe_forward_track_caller(closure_hir_id, expr.hir_id); (parameters, expr) @@ -358,6 +363,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring), constness: self.lower_constness(attrs, constness), explicit_captures, + captured_caller_location: None, }); hir::ExprKind::Closure(c) } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 6ec5903c5fb3b..52b2ad1cf7be3 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -2,7 +2,7 @@ use rustc_abi::ExternAbi; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; use rustc_attr_ir::target::Target; -use rustc_attr_ir::{AttributeKind, EiiImplResolution, find_attr}; +use rustc_attr_ir::{AttributeKind, EiiImplResolution, LangItem, find_attr}; use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{self as hir, HirId, ImplItemImplKind, LifetimeSource, PredicateOrigin}; @@ -268,12 +268,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let body_id = this.lower_maybe_coroutine_body( *fn_sig_span, span, + id, hir_id, decl, coroutine_marker, body.as_deref(), attrs, contract.as_deref(), + false, ); let itctx = ImplTraitContext::Universal; @@ -867,12 +869,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let body_id = self.lower_maybe_coroutine_body( sig.span, i.span, + i.id, hir_id, &sig.decl, sig.header.coroutine_marker, Some(body), attrs, contract.as_deref(), + false, ); let (generics, sig) = self.lower_method_sig( generics, @@ -1077,12 +1081,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let body_id = self.lower_maybe_coroutine_body( sig.span, i.span, + i.id, hir_id, &sig.decl, sig.header.coroutine_marker, body.as_deref(), attrs, contract.as_deref(), + is_in_trait_impl, ); let (generics, sig) = self.lower_method_sig( generics, @@ -1271,12 +1277,14 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, fn_decl_span: Span, span: Span, + node_id: NodeId, fn_id: hir::HirId, decl: &FnDecl, coroutine_marker: Option, body: Option<&Block>, attrs: &'hir [rustc_attr_ir::Attribute], contract: Option<&FnContract>, + is_in_trait_impl: bool, ) -> hir::BodyId { let Some(body) = body else { // Functions without a body are an error, except if this is an intrinsic. For those we @@ -1311,20 +1319,15 @@ impl<'hir> LoweringContext<'_, 'hir> { }; // FIXME(contracts): Support contracts on async fn. self.lower_body(|this| { - let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments( + this.lower_coroutine_body_with_moved_arguments( decl, |this| this.lower_block_expr(body), fn_decl_span, body.span, coroutine_marker, hir::CoroutineSource::Fn, - ); - - // FIXME(async_fn_track_caller): Can this be moved above? - let hir_id = expr.hir_id; - this.maybe_forward_track_caller(fn_id, hir_id); - - (parameters, expr) + this.should_track_caller_in_coroutine(fn_id, node_id, span, is_in_trait_impl), + ) }) } @@ -1340,6 +1343,7 @@ impl<'hir> LoweringContext<'_, 'hir> { body_span: Span, coroutine_marker: CoroutineMarker, coroutine_source: hir::CoroutineSource, + track_caller: bool, ) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>) { let mut parameters: Vec> = Vec::new(); let mut statements: Vec> = Vec::new(); @@ -1472,6 +1476,42 @@ impl<'hir> LoweringContext<'_, 'hir> { parameters.push(new_parameter); } + let (caller_location_init_stmt, caller_location_hir_id) = track_caller + .then(|| { + let ident = Ident::with_dummy_span(sym::__captured_caller_location); + let span = self.mark_span_with_reason( + DesugaringKind::CoroutineFnTrackCaller, + DUMMY_SP, + Some([sym::core_intrinsics].into()), + ); + + // Get the caller location inside the function/closure body, but outside the coroutine. + let (outer_pat, outer_pat_hir_id) = self.pat_ident(span, ident); + let outer_expr = self.expr_call_lang_item_fn(span, LangItem::CallerLocation, &[]); + let outer_let_stmt = self.stmt_let_pat( + None, + span, + Some(outer_expr), + outer_pat, + hir::LocalSource::AsyncFn, + ); + + // Capture the stored caller location in the coroutine. + let (inner_pat, _inner_pat_hir_id) = self.pat_ident(span, ident); + let inner_expr = self.expr_ident(span, ident, outer_pat_hir_id); + let inner_let_stmt = self.stmt_let_pat( + None, + span, + Some(inner_expr), + inner_pat, + hir::LocalSource::AsyncFn, + ); + statements.push(inner_let_stmt); + + (outer_let_stmt, outer_pat_hir_id) + }) + .unzip(); + let mkbody = |this: &mut LoweringContext<'_, 'hir>| { // Create a block from the user's function body: let user_body = lower_body(this); @@ -1505,7 +1545,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let closure_id = coroutine_marker.closure_id; - let coroutine_expr = self.make_desugared_coroutine_expr( + let coroutine_expr_kind = self.make_desugared_coroutine_expr( // The default capture mode here is by-ref. Later on during upvar analysis, // we will force the captured arguments to by-move, but for async closures, // we want to make sure that we avoid unnecessarily moving captures, or else @@ -1518,15 +1558,28 @@ impl<'hir> LoweringContext<'_, 'hir> { desugaring_kind, coroutine_source, mkbody, + caller_location_hir_id, ); - - let expr = hir::Expr { + let coroutine_expr = hir::Expr { hir_id: self.lower_node_id(closure_id), - kind: coroutine_expr, + kind: coroutine_expr_kind, span: self.lower_span(body_span), }; - (self.arena.alloc_from_iter(parameters), expr) + let body_expr = match caller_location_init_stmt { + Some(init_stmt) => { + let body_block = self.block_all( + DUMMY_SP, + self.arena.alloc_from_iter([init_stmt]), + Some(self.arena.alloc(coroutine_expr)), + ); + let body_expr_kind = hir::ExprKind::Block(body_block, None); + hir::Expr { hir_id: self.next_id(), kind: body_expr_kind, span: DUMMY_SP } + } + None => coroutine_expr, + }; + + (self.arena.alloc_from_iter(parameters), body_expr) } fn lower_method_sig( diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index 60890f7799290..acebb90f9a6c2 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -469,6 +469,10 @@ language_item_table! { // Experimental lang item for `Reflection and comptime`(https://goals.rust-lang.org/2025h2/reflection-and-comptime.html) FnPtr, sym::FnPtr, fn_ptr, Target::Struct, GenericRequirement::None; + + // Used in the desugaring of #[track_caller] on coroutine functions + // FIXME(closure_track_caller): Also use this in coroutine closures. + CallerLocation, sym::caller_location, caller_location, Target::Fn, GenericRequirement::Exact(0); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 4f4ed83be06a3..33ac0e2e15ff8 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1453,6 +1453,12 @@ pub struct Closure<'hir> { pub fn_arg_span: Option, pub kind: ClosureKind, pub explicit_captures: &'hir [ExplicitCapture], + /// If this is a coroutine that was desugared from a coroutine fn that's annotated + /// with `#[track_caller]`, then we track the binding that contains the caller location + /// outside the coroutine. + /// + /// FIXME(closure_track_caller): Also do this for coroutine closures + pub captured_caller_location: Option, } /// A HIR local that must be captured by value even if ordinary closure capture diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index ad96fe492e6e7..e79dd042e2022 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -913,6 +913,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) kind: _, constness: _, explicit_captures: _, + captured_caller_location: _, }) => { walk_list!(visitor, visit_generic_param, bound_generic_params); try_visit!(visitor.visit_fn(FnKind::Closure, fn_decl, body, *span, def_id)); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index d8c57bfbdb56b..9329c64d18807 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1680,6 +1680,7 @@ impl<'a> State<'a> { kind: _, def_id: _, explicit_captures: _, + captured_caller_location: _, }) => { self.print_closure_binder(binder, bound_generic_params); self.print_constness(constness); diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index a081bd1b65e92..22a4d120e0641 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1240,6 +1240,8 @@ pub enum DesugaringKind { source: bool, }, RangeExpr, + // `#[track_caller]` applied to a coroutine fn or coroutine closure + CoroutineFnTrackCaller, } impl DesugaringKind { @@ -1262,6 +1264,9 @@ impl DesugaringKind { "expression that expanded into a format string literal" } DesugaringKind::RangeExpr => "range expression", + DesugaringKind::CoroutineFnTrackCaller => { + "`#[track_caller]` on coroutine fn or coroutine closure" + } } } @@ -1282,6 +1287,7 @@ impl DesugaringKind { DesugaringKind::PatTyRange => value == "PatTyRange", DesugaringKind::FormatLiteral { .. } => value == "FormatLiteral", DesugaringKind::RangeExpr => value == "RangeExpr", + DesugaringKind::CoroutineFnTrackCaller => value == "CoroutineFnTrackCaller", } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a003f20e3512d..252f3fa31ee50 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -370,6 +370,7 @@ symbols! { __H, __S, __awaitee, + __captured_caller_location, __ensures_checker, __ret, __try_var, @@ -753,6 +754,7 @@ symbols! { copysignf64, copysignf128, core, + core_intrinsics, core_panic, core_panic_2015_macro, core_panic_macro, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index f44da9840b8e5..93d1e799c5f98 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -612,6 +612,7 @@ pub const fn assert_mem_uninitialized_valid(); #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] +#[lang = "caller_location"] pub const fn caller_location() -> &'static crate::panic::Location<'static>; /// Moves a value out of scope without running drop glue. diff --git a/src/tools/miri/tests/pass/async-panic-track-caller.rs b/src/tools/miri/tests/pass/async-panic-track-caller.rs index 03685d57c3b74..9eba732674c46 100644 --- a/src/tools/miri/tests/pass/async-panic-track-caller.rs +++ b/src/tools/miri/tests/pass/async-panic-track-caller.rs @@ -281,26 +281,23 @@ fn assert_panicked_at(f: impl FnOnce() + panic::UnwindSafe, line: u32) { assert!(result.is_ok()); } -// FIXME(async_fn_track_caller): Currently, #[track_caller] on an async function -// uses the location where the future is awaited or polled. -// The correct behavior as per T-lang is to use the location where the function is called. fn main() { assert_panicked_at(|| block_on(foo()), 61); assert_eq!(LINE.load(Relaxed), 59); #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| block_on(foo_track_caller()), 79); + assert_panicked_at(|| block_on(foo_track_caller()), 74); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 79); + assert_eq!(LINE.load(Relaxed), 72); #[cfg(any(cls, nofeat))] assert_panicked_at(|| block_on(foo_track_caller()), 74); #[cfg(any(cls, nofeat))] assert_eq!(LINE.load(Relaxed), 72); #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| block_on(foo_assoc()), 96); + assert_panicked_at(|| block_on(foo_assoc()), 90); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 96); + assert_eq!(LINE.load(Relaxed), 88); #[cfg(any(cls, nofeat))] assert_panicked_at(|| block_on(foo_assoc()), 90); #[cfg(any(cls, nofeat))] @@ -329,9 +326,9 @@ fn main() { assert_eq!(LINE.load(Relaxed), 123); #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| foo_manual_poll(), 138); + assert_panicked_at(|| foo_manual_poll(), 131); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 138); + assert_eq!(LINE.load(Relaxed), 129); #[cfg(any(cls, nofeat))] assert_panicked_at(|| foo_manual_poll(), 131); #[cfg(any(cls, nofeat))] @@ -345,18 +342,18 @@ fn main() { assert_eq!(LINE.load(Relaxed), 162); #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 205); + assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 171); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 205); + assert_eq!(LINE.load(Relaxed), 169); #[cfg(any(cls, nofeat))] assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 171); #[cfg(any(cls, nofeat))] assert_eq!(LINE.load(Relaxed), 169); #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 209); + assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 178); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 209); + assert_eq!(LINE.load(Relaxed), 176); #[cfg(any(cls, nofeat))] assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 178); #[cfg(any(cls, nofeat))] @@ -370,9 +367,9 @@ fn main() { assert_eq!(LINE.load(Relaxed), 188); #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| foo_gen_fn(), 230); + assert_panicked_at(|| foo_gen_fn(), 225); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 230); + assert_eq!(LINE.load(Relaxed), 223); #[cfg(any(cls, nofeat))] assert_panicked_at(|| foo_gen_fn(), 225); #[cfg(any(cls, nofeat))] diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index f0f9ed223af64..25e4f67c2486b 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -281,26 +281,23 @@ fn assert_panicked_at(f: impl FnOnce() + panic::UnwindSafe, line: u32) { assert!(result.is_ok()); } -// FIXME(async_fn_track_caller): Currently, #[track_caller] on an async function -// uses the location where the future is awaited or polled. -// The correct behavior as per T-lang is to use the location where the function is called. fn main() { assert_panicked_at(|| block_on(foo()), 61); assert_eq!(LINE.load(Relaxed), 59); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| block_on(foo_track_caller()), 79); + assert_panicked_at(|| block_on(foo_track_caller()), 74); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 79); + assert_eq!(LINE.load(Relaxed), 72); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| block_on(foo_track_caller()), 74); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_eq!(LINE.load(Relaxed), 72); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| block_on(foo_assoc()), 96); + assert_panicked_at(|| block_on(foo_assoc()), 90); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 96); + assert_eq!(LINE.load(Relaxed), 88); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| block_on(foo_assoc()), 90); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] @@ -325,9 +322,9 @@ fn main() { assert_eq!(LINE.load(Relaxed), 123); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| foo_manual_poll(), 138); + assert_panicked_at(|| foo_manual_poll(), 131); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 138); + assert_eq!(LINE.load(Relaxed), 129); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| foo_manual_poll(), 131); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] @@ -341,18 +338,18 @@ fn main() { assert_eq!(LINE.load(Relaxed), 162); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 205); + assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 171); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 205); + assert_eq!(LINE.load(Relaxed), 169); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 171); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_eq!(LINE.load(Relaxed), 169); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 209); + assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 178); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 209); + assert_eq!(LINE.load(Relaxed), 176); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 178); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] @@ -366,9 +363,9 @@ fn main() { assert_eq!(LINE.load(Relaxed), 188); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| foo_gen_fn(), 230); + assert_panicked_at(|| foo_gen_fn(), 225); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 230); + assert_eq!(LINE.load(Relaxed), 223); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| foo_gen_fn(), 225); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] From 7f2b3f55fe46927003c926f39156eadf4ba5673b Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Thu, 1 Oct 2026 21:42:51 +0700 Subject: [PATCH 5/9] Make `#[track_caller] async fn` track the caller, not the poller/awaiter. Part 2: MIR building --- compiler/rustc_middle/src/mir/mod.rs | 20 ++++++++++++++++++-- compiler/rustc_mir_build/src/builder/mod.rs | 20 +++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 19a6d2a31c4e3..5a9b2bf1f4069 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -165,6 +165,13 @@ pub struct CoroutineInfo<'tcx> { /// If this is a coroutine then record the type of source expression that caused this coroutine /// to be created. pub coroutine_kind: CoroutineKind, + + /// If this is a desugared coroutine that was desugared from a coroutine fn + /// that's annotated with `#[track_caller]`, then we record the field in the coroutine + /// that we can read the caller's `Location`. + /// + /// FIXME(closure_track_caller): Also use this for coroutine closures + pub captured_caller_location: Option, } impl<'tcx> CoroutineInfo<'tcx> { @@ -182,6 +189,7 @@ impl<'tcx> CoroutineInfo<'tcx> { coroutine_drop_async: None, coroutine_drop_proxy_async: None, coroutine_layout: None, + captured_caller_location: None, } } } @@ -682,8 +690,16 @@ impl<'tcx> Body<'tcx> { /// For a `Location` in this scope, determine what the "caller location" at that point is. This /// is interesting because of inlining: the `#[track_caller]` attribute of inlined functions - /// must be honored. Falls back to the `tracked_caller` value for `#[track_caller]` functions, - /// or the function's scope. + /// must be honored. + /// + /// Callers may pass `Some` in the `caller_location` argument to provide a different + /// return value to be used instead of `from_span(span_of_topmost_source)`. This is useful + /// if this `Body` is itself `#[track_caller]` + /// + /// This function does not handle the behavior of coroutines desugared from + /// `#[track_caller] async fn`, treating it as a non-`#[track_caller]` function. + /// Callers are responsible for handling this case, although we currently do not + /// inline such coroutines into anything. pub fn caller_location_span( &self, mut source_info: SourceInfo, diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 93114b59ed647..a3c8a99ee2f06 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -885,6 +885,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => return, }; + let captured_caller_location = + self.tcx.hir_node_by_def_id(self.def_id).expect_closure().captured_caller_location; + // In analyze_closure() in upvar.rs we gathered a list of upvars used by an // indexed closure and we stored in a map called closure_min_captures in TypeckResults // with the closure's DefId. Here, we run through that vec of UpvarIds for @@ -914,7 +917,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mutability = captured_place.mutability; let mut projs = closure_env_projs.clone(); - projs.push(ProjectionElem::Field(FieldIdx::new(i), ty)); + let field_idx = FieldIdx::new(i); + projs.push(ProjectionElem::Field(field_idx, ty)); match capture { ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {} ty::UpvarCapture::ByRef(..) => { @@ -934,6 +938,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { argument_index: None, }); + if captured_caller_location + .is_some_and(|hir_id| captured_place.get_root_variable() == hir_id) + { + let coroutine_info = self + .coroutine + .as_deref_mut() + .expect("caller location should only be captured in a coroutine"); + assert!( + coroutine_info.captured_caller_location.is_none(), + "should only have one captured_caller_location" + ); + coroutine_info.captured_caller_location = Some(field_idx); + } + let capture = Capture { captured_place, use_place, mutability }; (var_id, capture) }) From 2fe505ae518cb6d3038247f9b00b72412c09f3e4 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Thu, 1 Oct 2026 21:54:50 +0700 Subject: [PATCH 6/9] Make `#[track_caller] async fn` track the caller, not the poller/awaiter. Part 3: misc --- compiler/rustc_middle/src/ty/instance.rs | 12 ++++++++++++ compiler/rustc_mir_transform/src/check_inline.rs | 11 +++++++++++ tests/ui-fulldeps/rustc_public/check_track_caller.rs | 2 ++ 3 files changed, 25 insertions(+) diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 1a00293f8ffc6..66941ebedc30d 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -303,6 +303,18 @@ impl<'tcx> InstanceKind<'tcx> { } } + /// Checks if calling `self` requires passing an implicit caller location argument. + /// + /// Note that this returns `false` on the coroutine of a `#[track_caller] async fn`, + /// since calling the coroutine doesn't require any extra arguments, even if + /// the coroutine does "override" the caller location. + /// + /// In Miri, when reporting the stack trace of UB or other errors, + /// frames where this function returns true are skipped by default, + /// and `#[track_caller]` on an `async fn` is ignored for the generated coroutine. + /// This is because resolving the caller (as opposed to the poller) of a coroutine + /// requires reading the memory of the running program, and this memory can potentially + /// be corrupted by misbehaving user code. pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool { match *self { InstanceKind::Item(def_id) diff --git a/compiler/rustc_mir_transform/src/check_inline.rs b/compiler/rustc_mir_transform/src/check_inline.rs index bea0d5c81eb2f..4c1df8353ec9b 100644 --- a/compiler/rustc_mir_transform/src/check_inline.rs +++ b/compiler/rustc_mir_transform/src/check_inline.rs @@ -73,6 +73,17 @@ pub(super) fn is_inline_valid_on_body<'tcx>( _: TyCtxt<'tcx>, body: &Body<'tcx>, ) -> Result<(), &'static str> { + // We currently do not support inlining a callee which is the coroutine + // desugared from `#[track_caller] async fn`. + // This is because figuring out the caller_location of such coroutines + // requires accessing the argument of the `poll()` call. And inlining + // would cause us to lose track of where that argument is. + if let Some(coroutine) = &body.coroutine + && coroutine.captured_caller_location.is_some() + { + return Err("can't inline coroutines from `#[track_caller] async fn`"); + } + if body .basic_blocks .iter() diff --git a/tests/ui-fulldeps/rustc_public/check_track_caller.rs b/tests/ui-fulldeps/rustc_public/check_track_caller.rs index a9d396a479fa5..d7c3a7f11704a 100644 --- a/tests/ui-fulldeps/rustc_public/check_track_caller.rs +++ b/tests/ui-fulldeps/rustc_public/check_track_caller.rs @@ -105,6 +105,8 @@ fn test_caller_location_propagation(items: &[rustc_public::CrateItem]) { } /// Find the first call to a `#[track_caller]` function in the body and resolve its location. +/// +/// FIXME(async_fn_track_caller): What if the Location is stored inside a coroutine upvar? fn resolve_tracked_call_location(body: &Body, inherited: Option) -> MirConst { for bb in &body.blocks { if let TerminatorKind::Call { func, .. } = &bb.terminator.kind { From b7648f3ef69dbc0e04fa2000c18d90fea1236399 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Thu, 1 Oct 2026 21:55:23 +0700 Subject: [PATCH 7/9] Make `#[track_caller] async fn` track the caller, not the poller/awaiter. Part 4: codegen --- compiler/rustc_codegen_ssa/src/mir/block.rs | 49 +++++++++++++++---- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/mod.rs | 30 ++++++++++-- .../track-caller/panic-track-caller.rs | 44 +++++++++++------ 4 files changed, 96 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 8fa044a45de22..1adae640c3391 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -24,11 +24,10 @@ use tracing::{debug, info}; use super::operand::OperandRef; use super::operand::OperandValue::{self, Immediate, Pair, Ref, ZeroSized}; use super::place::{PlaceRef, PlaceValue}; -use super::{CachedLlbb, FunctionCx, LocalRef}; +use super::{CachedLlbb, CallerLocation, FunctionCx, IntrinsicResult, LocalRef}; use crate::base::{self, is_call_from_compiler_builtins_to_upstream_monomorphization}; use crate::common::{self, IntPredicate}; use crate::diagnostics::CompilerBuiltinsCannotCall; -use crate::mir::IntrinsicResult; use crate::traits::*; use crate::{MemFlags, meth}; @@ -795,7 +794,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(bx, terminator.source_info); // Get the location information. - let location = self.get_caller_location(bx, terminator.source_info).immediate(); + let location = self.codegen_caller_location(bx, terminator.source_info).immediate(); // Put together the arguments to the panic entry point. let (lang_item, args) = match msg { @@ -1441,7 +1440,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir_args + 1, "#[track_caller] fn's must have 1 more argument in their ABI than in their MIR: {instance:?} {fn_span:?} {fn_abi:?}", ); - let location = self.get_caller_location(bx, source_info); + let location = self.codegen_caller_location(bx, source_info); debug!( "codegen_call_terminator({:?}): location={:?} (fn_span {:?})", terminator, location, fn_span @@ -2178,15 +2177,47 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { tuple.layout.fields.count() } - pub(super) fn get_caller_location( + pub(super) fn codegen_caller_location( &mut self, bx: &mut Bx, source_info: mir::SourceInfo, ) -> OperandRef<'tcx, Bx::Value> { - self.mir.caller_location_span(source_info, self.caller_location, bx.tcx(), |span: Span| { - let const_loc = bx.tcx().span_as_caller_location(span); - OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty()) - }) + let caller_location = self.mir.caller_location_span( + source_info, + self.caller_location, + bx.tcx(), + |span: Span| { + let const_loc = bx.tcx().span_as_caller_location(span); + // We don't inline `#[track_caller] async fn` into anything, + // so in this case, we don't need to load the caller location from + // an upvar in the coroutine. + CallerLocation::Direct(OperandRef::from_const( + bx, + const_loc, + bx.tcx().caller_location_ty(), + )) + }, + ); + match caller_location { + CallerLocation::Direct(operand_ref) => operand_ref, + CallerLocation::Captured(idx) => { + let base = self.codegen_consume(bx, mir::Local::arg(0).into()); + // base is a `Pin<&mut Self>` or `&mut Self`, and we want the value of + // the `idx` field inside `Self`. + let is_pinned = self + .mir + .coroutine + .as_ref() + .expect("captured caller_location should only be in coroutines") + .coroutine_kind + .movability() + == ast::Movability::Static; + let coro_ref = if is_pinned { base.extract_field(self, bx, 0) } else { base }; // `&mut Self` + let coro_place = coro_ref.deref(bx.cx()); // `Self` + let location_place = coro_place.project_field(bx, idx.as_usize()); // `self.idx` + bx.load_operand(location_place) + } + } } fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> { diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index e6b70ce2ca77b..096e66e367a38 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -158,7 +158,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } sym::caller_location => { - let location = self.get_caller_location(bx, source_info); + let location = self.codegen_caller_location(bx, source_info); location.val } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index aefa8356536dc..f8c9182aee205 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -1,5 +1,6 @@ use std::iter; +use rustc_abi::FieldIdx; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -125,8 +126,19 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// This is `None` if no variable debuginfo/names are needed. per_local_var_debug_info: Option>, - /// Caller location propagated if this function has `#[track_caller]`. - caller_location: Option>, + /// Where to access the caller location (for the parent-most body if there's inlining) + /// when there's `#[track_caller]`. + caller_location: Option>, +} + +#[derive(Clone, Copy)] +enum CallerLocation<'tcx, Value> { + /// Typical case: This function has `#[track_caller]`. + /// We track the caller location argument directly. + Direct(OperandRef<'tcx, Value>), + /// Used if we're in a desugared coroutine inside a coroutine fn. + /// We track the field index inside `Self` that stores the caller location. + Captured(FieldIdx), } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -634,7 +646,15 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx.cached_llbbs[mir::START_BLOCK] = CachedLlbb::Some(bx.llbb()); } - if fx.instance.def.requires_caller_location(bx.tcx()) { + if let Some(coro_info) = mir.coroutine.as_deref() + && let Some(captured_caller_location_idx) = coro_info.captured_caller_location + { + assert!( + !fx.instance.def.requires_caller_location(bx.tcx()), + "should have only one source of truth for caller_location" + ); + fx.caller_location = Some(CallerLocation::Captured(captured_caller_location_idx)); + } else if fx.instance.def.requires_caller_location(bx.tcx()) { let mir_args = if let Some(num_untupled) = num_untupled { // Subtract off the tupled argument that gets 'expanded' args.len() - 1 + num_untupled @@ -654,11 +674,11 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( _ => bug!("caller location must be PassMode::Direct, found {:?}", arg.mode), } - fx.caller_location = Some(OperandRef { + fx.caller_location = Some(CallerLocation::Direct(OperandRef { val: OperandValue::Immediate(bx.get_param(llarg_idx)), layout: arg.layout, move_annotation: None, - }); + })); } args diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index 25e4f67c2486b..99dec25fdca26 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -286,23 +286,27 @@ fn main() { assert_eq!(LINE.load(Relaxed), 59); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| block_on(foo_track_caller()), 74); + assert_panicked_at(|| block_on(foo_track_caller()), 78); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 72); + assert_eq!(LINE.load(Relaxed), 78); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| block_on(foo_track_caller()), 74); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_eq!(LINE.load(Relaxed), 72); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| block_on(foo_assoc()), 90); + assert_panicked_at(|| block_on(foo_assoc()), 95); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 88); + assert_eq!(LINE.load(Relaxed), 95); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| block_on(foo_assoc()), 90); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_eq!(LINE.load(Relaxed), 88); + // FIXME(closure_track_caller): Currently, #[track_caller] on an async closure + // uses the location where the future is awaited or polled. + // It should be changed to use the location where the closure is called, + // so the behavior matches that of `async fn`. #[cfg(any(afn_cls, afn_cls_opt))] assert_panicked_at(|| block_on(foo_closure()), 110); #[cfg(any(afn_cls, afn_cls_opt))] @@ -322,9 +326,9 @@ fn main() { assert_eq!(LINE.load(Relaxed), 123); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| foo_manual_poll(), 131); + assert_panicked_at(|| foo_manual_poll(), 135); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 129); + assert_eq!(LINE.load(Relaxed), 135); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| foo_manual_poll(), 131); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] @@ -333,39 +337,50 @@ fn main() { assert_panicked_at(|| block_on(foo_trait_attr_nowhere()), 159); assert_eq!(LINE.load(Relaxed), 157); - // FIXME(async_fn_track_caller): This case just currently doesn't work. + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_panicked_at(|| block_on(foo_trait_attr_in_trait()), 200); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 200); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| block_on(foo_trait_attr_in_trait()), 164); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_eq!(LINE.load(Relaxed), 162); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 171); + assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 204); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 169); + assert_eq!(LINE.load(Relaxed), 204); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 171); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_eq!(LINE.load(Relaxed), 169); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 178); + assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 208); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 176); + assert_eq!(LINE.load(Relaxed), 208); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 178); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_eq!(LINE.load(Relaxed), 176); - // FIXME(async_fn_track_caller): This case just currently doesn't work. + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_panicked_at(|| block_on(foo_rpit_in_trait()), 213); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 213); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| block_on(foo_rpit_in_trait()), 184); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_eq!(LINE.load(Relaxed), 182); assert_panicked_at(|| block_on(foo_rpit_in_impl()), 190); assert_eq!(LINE.load(Relaxed), 188); + // FIXME(gen_blocks): Decide if this behavior is correct. #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_panicked_at(|| foo_gen_fn(), 225); + assert_panicked_at(|| foo_gen_fn(), 229); #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] - assert_eq!(LINE.load(Relaxed), 223); + assert_eq!(LINE.load(Relaxed), 229); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] assert_panicked_at(|| foo_gen_fn(), 225); #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] @@ -376,6 +391,7 @@ fn main() { #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] assert_eq!(LINE.load(Relaxed), 244); + // FIXME(coroutines): This behavior is inconsistent with async blocks. #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] assert_panicked_at(|| foo_coroutine(), 260); #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] From 173c37f66b13623e91ec2abc70900a128e06fdba Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Wed, 7 Oct 2026 21:50:17 +0700 Subject: [PATCH 8/9] Make `#[track_caller] async fn` track the caller, not the poller/awaiter. Part 5: Miri --- .../rustc_codegen_cranelift/src/common.rs | 14 ++-- compiler/rustc_codegen_ssa/src/mir/block.rs | 2 +- .../src/const_eval/machine.rs | 59 +++++++++++----- .../rustc_const_eval/src/interpret/call.rs | 3 +- .../src/interpret/eval_context.rs | 70 +++++++++++++++++-- .../src/interpret/intrinsics.rs | 21 ++++-- .../rustc_const_eval/src/interpret/mod.rs | 1 + .../rustc_const_eval/src/interpret/stack.rs | 14 +++- compiler/rustc_const_eval/src/lib.rs | 1 + compiler/rustc_middle/src/mir/mod.rs | 4 +- .../tests/pass/async-panic-track-caller.rs | 39 +++++++---- 11 files changed, 175 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 30bd2f28af53f..94e42f1e483e9 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -421,10 +421,16 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { } pub(crate) fn get_caller_location(&mut self, source_info: mir::SourceInfo) -> CValue<'tcx> { - self.mir.caller_location_span(source_info, self.caller_location, self.tcx, |span| { - let const_loc = self.tcx.span_as_caller_location(span); - crate::constant::codegen_const_value(self, const_loc, self.tcx.caller_location_ty()) - }) + let caller_location = self.caller_location; + self.mir.caller_location_span( + source_info, + || caller_location, + self.tcx, + |span| { + let const_loc = self.tcx.span_as_caller_location(span); + crate::constant::codegen_const_value(self, const_loc, self.tcx.caller_location_ty()) + }, + ) } } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 1adae640c3391..199892ae60bd7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -2184,7 +2184,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) -> OperandRef<'tcx, Bx::Value> { let caller_location = self.mir.caller_location_span( source_info, - self.caller_location, + || self.caller_location, bx.tcx(), |span: Span| { let const_loc = bx.tcx().span_as_caller_location(span); diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 9efe2223c3bb1..e2426d847359b 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -17,15 +17,15 @@ use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::{self, FieldInfo, ScalarInt, Ty, TyCtxt}; use rustc_middle::{mir, throw_machine_stop}; -use rustc_span::{Span, Symbol, bug, span_bug, sym}; +use rustc_span::{Symbol, bug, span_bug, sym}; use rustc_target::callconv::FnAbi; use tracing::debug; use super::error::*; use crate::diagnostics::{LongRunning, LongRunningWarn}; use crate::interpret::{ - self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, + self, AllocId, AllocInit, AllocRange, CallerLocation, ConstAllocation, CtfeProvenance, FnArg, + Frame, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, RetagMode, Scalar, compile_time_machine, ensure_monomorphic_enough, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_format, throw_unsup, throw_unsup_format, type_implements_dyn_trait, @@ -212,18 +212,41 @@ impl interpret::MayLeak for ! { } impl<'tcx> CompileTimeInterpCx<'tcx> { - fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) { - let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); - let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); - - use rustc_span::RemapPathScopeComponents; - ( - Symbol::intern( - &caller.file.name.display(RemapPathScopeComponents::DIAGNOSTICS).to_string_lossy(), - ), - u32::try_from(caller.line).unwrap(), - u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(), - ) + fn caller_location_triple(&self) -> InterpResult<'tcx, (Symbol, u32, u32)> { + interp_ok(match self.caller_location()? { + CallerLocation::Direct(span) => { + let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); + let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); + + use rustc_span::RemapPathScopeComponents; + ( + Symbol::intern( + &caller + .file + .name + .display(RemapPathScopeComponents::DIAGNOSTICS) + .to_string_lossy(), + ), + u32::try_from(caller.line).unwrap(), + u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(), + ) + } + CallerLocation::Memory(place) => { + // `place` refers to a `Location`. Read its fields. + let filename = + Symbol::intern(self.read_str(&self.deref_pointer(&self.project_field( + &self.project_field(&place, FieldIdx::from_usize(0))?, + FieldIdx::from_usize(0), + )?)?)?); + let line = self + .read_scalar(&self.project_field(&place, FieldIdx::from_usize(1))?)? + .to_u32()?; + let col = self + .read_scalar(&self.project_field(&place, FieldIdx::from_usize(2))?)? + .to_u32()?; + (filename, line, col) + } + }) } /// "Intercept" a function call, because we have something special to do for it. @@ -253,8 +276,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { } let msg = Symbol::intern(self.read_str(&msg_place)?); - let span = self.caller_location(); - let (file, line, col) = self.location_triple_for_span(span); + let (file, line, col) = self.caller_location_triple()?; return Err(ConstEvalErrKind::Panic { msg, file, line, col }).into(); } else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) { // For panic_fmt, call const_panic_fmt instead. @@ -463,8 +485,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> { let msg = Symbol::intern(msg); - let span = ecx.caller_location(); - let (file, line, col) = ecx.location_triple_for_span(span); + let (file, line, col) = ecx.caller_location_triple()?; Err(ConstEvalErrKind::Panic { msg, file, line, col }).into() } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index f1b551441a675..99e811c663f05 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -464,7 +464,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let def_id = instance.def_id(); assert!(!with_caller_location || callee_receives_caller_location); - let caller_location = callee_receives_caller_location.then(|| self.caller_location()); + let caller_location = + if callee_receives_caller_location { Some(self.caller_location()?) } else { None }; // The first order of business is to figure out the callee signature. // However, that requires the list of variadic arguments. diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index d6d80c3e3506a..f5a9a3de6b615 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1,9 +1,9 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; -use std::convert::identity; use either::{Left, Right}; -use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; +use rustc_abi::{Align, FieldIdx, HasDataLayout, Size, TargetDataLayout}; +use rustc_ast::ast::Movability; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_middle::mir; @@ -22,9 +22,9 @@ use rustc_target::callconv::FnAbi; use tracing::trace; use super::{ - Frame, FrameInfo, GlobalId, InterpErrorKind, InterpResult, MPlaceTy, Machine, MemPlaceMeta, - Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, err_inval, interp_ok, - throw_inval, throw_ub, throw_ub_format, + CallerLocation, Frame, FrameInfo, GlobalId, InterpErrorKind, InterpResult, MPlaceTy, Machine, + MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, + err_inval, interp_ok, throw_inval, throw_ub, throw_ub_format, }; use crate::{enter_trace_span, util}; @@ -380,7 +380,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Grabs the implicit caller location argument, if there is one, /// or falls back to the "current" span. This matches the `caller_location` intrinsic, /// and is primarily intended for the panic machinery. - pub(crate) fn caller_location(&self) -> Span { + pub(crate) fn caller_location( + &self, + ) -> InterpResult<'tcx, CallerLocation<'tcx, M::Provenance>> { let frame = self.frame(); // Assert that the frame we look at is actually executing code currently @@ -404,7 +406,61 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { source_info.span = fn_span; } - frame.body.caller_location_span(source_info, frame.track_caller_arg, *self.tcx, identity) + // The caller location of the top-level function, among the functions inlined + // into this frame. + // We need to compute this lazily, as this computation can cause UB for + // `#[track_caller] async fn`. Computing it eagerly would cause detection of UB + // in the case where a non-`#[track_caller]` function is inlined into a + // `#[track_caller] async fn`, the coroutine contains a dangling `&Location`, + // and the `&Location` was not used before inlining. + // (Note that it's fine to not use the `&Location` after inlining even if it's + // used before inlining. This just means that optimization causes us to miss UB.) + // Importantly, we don't actually dereference the `&Location` here, + // as this is not required to propagate it further as an implicit + // caller location argument. + let top_level_caller_location = || { + if let Some(coroutine) = &frame.body.coroutine + && let Some(field_idx) = coroutine.captured_caller_location + { + assert!( + !frame.instance.def.requires_caller_location(*self.tcx), + "should have only one source of truth for caller_location" + ); + + let is_pinned = coroutine.coroutine_kind.movability() == Movability::Static; + + Some(try { + // `Pin<&mut Self>` or `&mut Self` + let base_op = self.local_to_op(mir::Local::arg(0), None)?; + // `&mut Self` + let coro_ref_op = if is_pinned { + self.project_field(&base_op, FieldIdx::from_usize(0))? + } else { + base_op + }; + // `Self` + let coro_place = self.deref_pointer(&coro_ref_op)?; + // `&Location` + let location_ref_place = self.project_field(&coro_place, field_idx)?; + // `Location` + let location_place = self.deref_pointer(&location_ref_place)?; + CallerLocation::Memory(location_place) + }) + } else if frame.instance.def.requires_caller_location(*self.tcx) { + Some(interp_ok( + frame + .track_caller_arg + .clone() + .expect("should have received track_caller argument"), + )) + } else { + None + } + }; + + frame.body.caller_location_span(source_info, top_level_caller_location, *self.tcx, |span| { + interp_ok(CallerLocation::Direct(span)) + }) } /// Returns the actual dynamic size and alignment of the place at the given type. diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index d66f4cf900eca..05bd56f9a7031 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -25,7 +25,7 @@ use super::{ PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance, Scalar, err_ub_format, err_unsup_format, interp_ok, throw_inval, throw_ub, throw_ub_format, }; -use crate::interpret::{MPlaceTy, Writeable}; +use crate::interpret::{CallerLocation, MPlaceTy, Writeable}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum MulAddType { @@ -293,10 +293,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } sym::caller_location => { - let span = self.caller_location(); - let val = self.tcx.span_as_caller_location(span); - let val = - self.const_val_to_op(val, self.tcx.caller_location_ty(), Some(dest.layout))?; + let val = match self.caller_location()? { + CallerLocation::Direct(span) => self.const_val_to_op( + self.tcx.span_as_caller_location(span), + self.tcx.caller_location_ty(), + Some(dest.layout), + )?, + CallerLocation::Memory(place) => self + .mplace_to_imm_ptr( + &place, + Some( + self.tcx.erase_and_anonymize_regions(self.tcx.caller_location_ty()), + ), + )? + .into(), + }; self.copy_op(&val, dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 1eceff75e092b..e833ff662293e 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -40,6 +40,7 @@ pub use self::operator::AtomicRmwOp; pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable}; use self::place::{MemPlace, Place}; pub use self::projection::{OffsetMode, Projectable}; +pub(crate) use self::stack::CallerLocation; pub use self::stack::{Frame, FrameInfo, LocalState, ReturnContinuation}; pub use self::util::EnteredTraceSpan; pub(crate) use self::util::{ diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 6d5c7a78d9f73..91a84b561c27e 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -97,7 +97,7 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { pub(super) va_list: Vec>, /// The implicit caller location argument that is passed by this function's caller, if any. - pub(super) track_caller_arg: Option, + pub(super) track_caller_arg: Option>, /// The span of the `tracing` crate is stored here. /// When the guard is dropped, the span is exited. This gives us @@ -115,6 +115,18 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { pub(super) loc: Either, } +/// Information necessary to figure out the caller_location +#[derive(Clone)] +pub(crate) enum CallerLocation<'tcx, Prov: Provenance> { + /// Simple case: This span was created when we called a `#[track_caller]` frame + /// (other than an async fn) from a normal frame. We know the span from just inspecting + /// the stack at the point of this call. + Direct(Span), + /// A place containing a `std::panic::Location`. This is used for `#[track_caller] async fn`, + /// which captures a `&std::panic::Location` in the coroutine. + Memory(MPlaceTy<'tcx, Prov>), +} + /// Where and how to continue when returning/unwinding from the current function. #[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these pub enum ReturnContinuation { diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 981813f13c97f..ccf88b6df765c 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -4,6 +4,7 @@ #![feature(deref_patterns)] #![feature(slice_ptr_get)] #![feature(trait_alias)] +#![feature(try_blocks)] #![feature(unqualified_local_imports)] #![feature(yeet_expr)] #![warn(unqualified_local_imports)] diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 5a9b2bf1f4069..e69dce03f621e 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -703,7 +703,7 @@ impl<'tcx> Body<'tcx> { pub fn caller_location_span( &self, mut source_info: SourceInfo, - caller_location: Option, + caller_location: impl FnOnce() -> Option, tcx: TyCtxt<'tcx>, from_span: impl FnOnce(Span) -> T, ) -> T { @@ -727,7 +727,7 @@ impl<'tcx> Body<'tcx> { } // No inlined `SourceScope`s, or all of them were `#[track_caller]`. - caller_location.unwrap_or_else(|| from_span(source_info.span)) + caller_location().unwrap_or_else(|| from_span(source_info.span)) } #[track_caller] diff --git a/src/tools/miri/tests/pass/async-panic-track-caller.rs b/src/tools/miri/tests/pass/async-panic-track-caller.rs index 9eba732674c46..9dba33ebbf64f 100644 --- a/src/tools/miri/tests/pass/async-panic-track-caller.rs +++ b/src/tools/miri/tests/pass/async-panic-track-caller.rs @@ -286,18 +286,18 @@ fn main() { assert_eq!(LINE.load(Relaxed), 59); #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| block_on(foo_track_caller()), 74); + assert_panicked_at(|| block_on(foo_track_caller()), 78); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 72); + assert_eq!(LINE.load(Relaxed), 78); #[cfg(any(cls, nofeat))] assert_panicked_at(|| block_on(foo_track_caller()), 74); #[cfg(any(cls, nofeat))] assert_eq!(LINE.load(Relaxed), 72); #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| block_on(foo_assoc()), 90); + assert_panicked_at(|| block_on(foo_assoc()), 95); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 88); + assert_eq!(LINE.load(Relaxed), 95); #[cfg(any(cls, nofeat))] assert_panicked_at(|| block_on(foo_assoc()), 90); #[cfg(any(cls, nofeat))] @@ -326,9 +326,9 @@ fn main() { assert_eq!(LINE.load(Relaxed), 123); #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| foo_manual_poll(), 131); + assert_panicked_at(|| foo_manual_poll(), 135); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 129); + assert_eq!(LINE.load(Relaxed), 135); #[cfg(any(cls, nofeat))] assert_panicked_at(|| foo_manual_poll(), 131); #[cfg(any(cls, nofeat))] @@ -338,38 +338,50 @@ fn main() { assert_eq!(LINE.load(Relaxed), 157); // FIXME(async_fn_track_caller): This case just currently doesn't work. + #[cfg(any(afn, afn_cls))] + assert_panicked_at(|| block_on(foo_trait_attr_in_trait()), 200); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 200); + #[cfg(any(cls, nofeat))] assert_panicked_at(|| block_on(foo_trait_attr_in_trait()), 164); + #[cfg(any(cls, nofeat))] assert_eq!(LINE.load(Relaxed), 162); #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 171); + assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 204); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 169); + assert_eq!(LINE.load(Relaxed), 204); #[cfg(any(cls, nofeat))] assert_panicked_at(|| block_on(foo_trait_attr_in_impl()), 171); #[cfg(any(cls, nofeat))] assert_eq!(LINE.load(Relaxed), 169); #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 178); + assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 208); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 176); + assert_eq!(LINE.load(Relaxed), 208); #[cfg(any(cls, nofeat))] assert_panicked_at(|| block_on(foo_trait_attr_in_both()), 178); #[cfg(any(cls, nofeat))] assert_eq!(LINE.load(Relaxed), 176); - // FIXME(async_fn_track_caller): This case just currently doesn't work. + #[cfg(any(afn, afn_cls))] + assert_panicked_at(|| block_on(foo_rpit_in_trait()), 213); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 213); + #[cfg(any(cls, nofeat))] assert_panicked_at(|| block_on(foo_rpit_in_trait()), 184); + #[cfg(any(cls, nofeat))] assert_eq!(LINE.load(Relaxed), 182); assert_panicked_at(|| block_on(foo_rpit_in_impl()), 190); assert_eq!(LINE.load(Relaxed), 188); + // FIXME(gen_blocks): Decide if this behavior is correct. #[cfg(any(afn, afn_cls))] - assert_panicked_at(|| foo_gen_fn(), 225); + assert_panicked_at(|| foo_gen_fn(), 229); #[cfg(any(afn, afn_cls))] - assert_eq!(LINE.load(Relaxed), 223); + assert_eq!(LINE.load(Relaxed), 229); #[cfg(any(cls, nofeat))] assert_panicked_at(|| foo_gen_fn(), 225); #[cfg(any(cls, nofeat))] @@ -380,6 +392,7 @@ fn main() { #[cfg(any(cls, afn_cls))] assert_eq!(LINE.load(Relaxed), 244); + // FIXME(coroutines): This behavior is inconsistent with async blocks. #[cfg(any(cls, afn_cls))] assert_panicked_at(|| foo_coroutine(), 260); #[cfg(any(cls, afn_cls))] From f857e6670c0a444e3930f68c0646455e0246d651 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Thu, 8 Oct 2026 18:53:02 +0700 Subject: [PATCH 9/9] Make `#[track_caller] async fn` track the caller, not the poller/awaiter. Part 6: Cranelift --- .../rustc_codegen_cranelift/src/abi/mod.rs | 16 ++++-- compiler/rustc_codegen_cranelift/src/base.rs | 12 ++--- .../rustc_codegen_cranelift/src/common.rs | 53 ++++++++++++++++--- .../src/intrinsics/mod.rs | 2 +- 4 files changed, 64 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 7f46b19f7568f..0c43622eb1c66 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -325,7 +325,15 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ .collect::, Ty<'tcx>)>>(); assert!(fx.caller_location.is_none()); - if fx.instance.def.requires_caller_location(fx.tcx) { + if let Some(coro_info) = fx.mir.coroutine.as_deref() + && let Some(captured_caller_location_idx) = coro_info.captured_caller_location + { + assert!( + !fx.instance.def.requires_caller_location(fx.tcx), + "should have only one source of truth for caller_location" + ); + fx.caller_location = Some(CallerLocation::Captured(captured_caller_location_idx)); + } else if fx.instance.def.requires_caller_location(fx.tcx) { // Store caller location for `#[track_caller]`. let arg_abi = arg_abis_iter.next().unwrap(); let param = cvalue_for_param(fx, None, None, arg_abi, &mut block_params_iter).unwrap(); @@ -333,7 +341,7 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ !param.is_underaligned_pointee, "caller location argument should not be underaligned", ); - fx.caller_location = Some(param.value); + fx.caller_location = Some(CallerLocation::Direct(param.value)); } assert_eq!(arg_abis_iter.next(), None, "ArgAbi left behind for {:?}", fx.fn_abi); @@ -557,7 +565,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( // Pass the caller location for `#[track_caller]`. if instance.is_some_and(|inst| inst.def.requires_caller_location(fx.tcx)) { - let caller_location = fx.get_caller_location(source_info); + let caller_location = fx.codegen_caller_location(source_info); args.push(CallArgument { value: caller_location, is_owned: false }); } @@ -811,7 +819,7 @@ pub(crate) fn codegen_drop<'tcx>( if drop_instance.def.requires_caller_location(fx.tcx) { // Pass the caller location for `#[track_caller]`. - let caller_location = fx.get_caller_location(source_info); + let caller_location = fx.codegen_caller_location(source_info); call_args.extend(adjust_arg_for_abi( fx, caller_location, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index e752189130290..747d70dd80f0c 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -391,7 +391,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { AssertKind::BoundsCheck { len, index } => { let len = codegen_operand(fx, len).load_scalar(fx); let index = codegen_operand(fx, index).load_scalar(fx); - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, @@ -404,7 +404,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { AssertKind::MisalignedPointerDereference { required, found } => { let required = codegen_operand(fx, required).load_scalar(fx); let found = codegen_operand(fx, found).load_scalar(fx); - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, @@ -415,7 +415,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ); } AssertKind::NullPointerDereference => { - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, @@ -426,7 +426,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ) } AssertKind::NullReferenceConstructed => { - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, @@ -438,7 +438,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { } AssertKind::InvalidEnumConstruction(source) => { let source = codegen_operand(fx, source).load_scalar(fx); - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, @@ -449,7 +449,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ) } _ => { - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 94e42f1e483e9..b636a5aebb600 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -1,6 +1,7 @@ use cranelift_codegen::isa::TargetFrontendConfig; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; use rustc_abi::{Float, Integer, Primitive}; +use rustc_ast::Movability; use rustc_index::IndexVec; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::layout::{ @@ -288,8 +289,9 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { pub(crate) block_map: IndexVec, pub(crate) local_map: IndexVec>, - /// When `#[track_caller]` is used, the implicit caller location is stored in this variable. - pub(crate) caller_location: Option>, + /// Where to access the caller location (for the parent-most body if there's inlining) + /// when there's `#[track_caller]`. + pub(crate) caller_location: Option>, /// During cleanup the exception pointer will be stored in this variable. pub(crate) exception_slot: Variable, @@ -300,6 +302,16 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { pub(crate) inline_asm_index: u32, } +#[derive(Clone, Copy)] +pub(crate) enum CallerLocation<'tcx> { + /// Typical case: This function has `#[track_caller]`. + /// We track the caller location argument directly. + Direct(CValue<'tcx>), + /// Used if we're in a desugared coroutine inside a coroutine fn. + /// We track the field index inside `Self` that stores the caller location. + Captured(FieldIdx), +} + impl<'tcx> LayoutOfHelpers<'tcx> for FunctionCx<'_, '_, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { @@ -420,17 +432,42 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { } } - pub(crate) fn get_caller_location(&mut self, source_info: mir::SourceInfo) -> CValue<'tcx> { - let caller_location = self.caller_location; - self.mir.caller_location_span( + pub(crate) fn codegen_caller_location(&mut self, source_info: mir::SourceInfo) -> CValue<'tcx> { + let self_caller_location = self.caller_location; + let caller_location = self.mir.caller_location_span( source_info, - || caller_location, + || self_caller_location, self.tcx, |span| { let const_loc = self.tcx.span_as_caller_location(span); - crate::constant::codegen_const_value(self, const_loc, self.tcx.caller_location_ty()) + CallerLocation::Direct(crate::constant::codegen_const_value( + self, + const_loc, + self.tcx.caller_location_ty(), + )) }, - ) + ); + match caller_location { + CallerLocation::Direct(value) => value, + CallerLocation::Captured(idx) => { + let base = self.local_map[Local::arg(0)]; + // base is a `Pin<&mut Self>` or `&mut Self`, and we want the value of + // the `idx` field inside `Self`. + let is_pinned = self + .mir + .coroutine + .as_ref() + .expect("captured caller_location should only be in coroutines") + .coroutine_kind + .movability() + == Movability::Static; + let coro_ref = + if is_pinned { base.place_field(self, FieldIdx::from_usize(0)) } else { base }; // `&mut Self` + let coro_place = coro_ref.place_deref(self); // `Self` + let location_place = coro_place.place_field(self, idx); // `self.idx` + location_place.to_cvalue(self) + } + } } } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index cf1c1f027e7f5..a64bc2702d418 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -812,7 +812,7 @@ fn codegen_regular_intrinsic_call<'tcx>( sym::caller_location => { intrinsic_args!(fx, args => (); intrinsic); - let caller_location = fx.get_caller_location(source_info); + let caller_location = fx.codegen_caller_location(source_info); ret.write_cvalue(fx, caller_location); }