diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 68d5278c85ef1..124d2cac24b10 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -203,6 +203,13 @@ fn split_attrs( foreign_item_attributes.push(attr.clone()); macro_attributes.push(attr); } + // `#[track_caller]` goes on the foreign item only: it's the symbol callers link + // against, so it must carry the flag for call sites to pass the caller location. + // Implementations derive it during codegen (see `EiiImpls` in `codegen_attrs.rs`), + // so it must not be routed onto the default impl here. + Some(sym::track_caller) => { + foreign_item_attributes.push(attr); + } // Doc attributes should be forwarded to the macro and the foreign item, since those are // the two items you interact with as a user. // FIXME: idk yet how EIIs show up in docs, might want to customize diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 4d271447746c1..bc5cb515db34e 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -264,6 +264,18 @@ fn process_builtin_attrs( Visibility::Default, )); codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; + + // If the declaration is `#[track_caller]`, derive it onto the implementation + // too. The shim that forwards to this impl (see `add_function_aliases`) takes + // its ABI from the impl's `fn_abi`, so every impl must agree on whether the + // caller-location argument is present, otherwise it would be silently dropped. + if tcx + .codegen_fn_attrs(foreign_item) + .flags + .contains(CodegenFnAttrFlags::TRACK_CALLER) + { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; + } } } AttributeKind::ThreadLocal => { diff --git a/tests/ui/eii/linking/auxiliary/track_caller_cross_other_crate.rs b/tests/ui/eii/linking/auxiliary/track_caller_cross_other_crate.rs new file mode 100644 index 0000000000000..5a9c1aa2ac937 --- /dev/null +++ b/tests/ui/eii/linking/auxiliary/track_caller_cross_other_crate.rs @@ -0,0 +1,7 @@ +//@ no-prefer-dynamic +#![crate_type = "rlib"] +#![feature(extern_item_impls)] + +#[track_caller] +#[eii(tcross)] +pub fn tcross(x: u64); diff --git a/tests/ui/eii/linking/track_caller_cross_crate.rs b/tests/ui/eii/linking/track_caller_cross_crate.rs new file mode 100644 index 0000000000000..2e16ec7b9fb23 --- /dev/null +++ b/tests/ui/eii/linking/track_caller_cross_crate.rs @@ -0,0 +1,29 @@ +//@ run-pass +//@ check-run-results +//@ aux-build: track_caller_cross_other_crate.rs +//@ compile-flags: -O +//@ ignore-backends: gcc +//@ ignore-windows +// Tests that `#[track_caller]` on an EII declaration in one crate is derived +// onto an explicit implementation in another crate. + +extern crate track_caller_cross_other_crate as decl; + +use std::panic::Location; +use std::sync::atomic::{AtomicU32, Ordering}; + +static LAST_LINE: AtomicU32 = AtomicU32::new(0); + +#[decl::tcross] +fn my_impl(x: u64) { + LAST_LINE.store(Location::caller().line(), Ordering::SeqCst); + println!("impl {x}"); +} + +fn main() { + my_impl(1); + assert_eq!(LAST_LINE.load(Ordering::SeqCst), line!() - 1); + + decl::tcross(2); + assert_eq!(LAST_LINE.load(Ordering::SeqCst), line!() - 1); +} diff --git a/tests/ui/eii/linking/track_caller_cross_crate.run.stdout b/tests/ui/eii/linking/track_caller_cross_crate.run.stdout new file mode 100644 index 0000000000000..38834e7a16bd4 --- /dev/null +++ b/tests/ui/eii/linking/track_caller_cross_crate.run.stdout @@ -0,0 +1,2 @@ +impl 1 +impl 2 diff --git a/tests/ui/eii/track_caller.rs b/tests/ui/eii/track_caller.rs new file mode 100644 index 0000000000000..62f48467839af --- /dev/null +++ b/tests/ui/eii/track_caller.rs @@ -0,0 +1,36 @@ +//@ run-pass +//@ ignore-backends: gcc +//@ ignore-windows +// Tests that `#[track_caller]` on an EII declaration is threaded through both +// the default impl (no override) and an explicit override (which does not +// repeat `#[track_caller]` — it is derived during codegen so the shim ABI matches). + +#![feature(extern_item_impls)] + +use std::panic::Location; +use std::sync::atomic::{AtomicU32, Ordering}; + +static LAST_LINE: AtomicU32 = AtomicU32::new(0); + +#[track_caller] +#[eii] +fn decl_default(_x: u64) { + LAST_LINE.store(Location::caller().line(), Ordering::SeqCst); +} + +#[track_caller] +#[eii] +fn decl_override(_x: u64); + +#[decl_override] +fn explicit_override(_x: u64) { + LAST_LINE.store(Location::caller().line(), Ordering::SeqCst); +} + +fn main() { + decl_default(1); + assert_eq!(LAST_LINE.load(Ordering::SeqCst), line!() - 1); + + decl_override(2); + assert_eq!(LAST_LINE.load(Ordering::SeqCst), line!() - 1); +} diff --git a/tests/ui/eii/track_caller_errors.rs b/tests/ui/eii/track_caller_errors.rs new file mode 100644 index 0000000000000..53b55f5b4eaef --- /dev/null +++ b/tests/ui/eii/track_caller_errors.rs @@ -0,0 +1,18 @@ +// `#[track_caller]` is derived onto EII implementations during codegen, so +// implementations must not repeat it manually. +#![feature(extern_item_impls)] + +#[eii] +fn decl1(x: u64) { + println!("default {x}"); +} + +#[track_caller] //~ ERROR `#[decl1]` is not allowed to have `#[track_caller]` +#[decl1] +fn impl1(x: u64) { + println!("explicit {x}"); +} + +fn main() { + decl1(4); +} diff --git a/tests/ui/eii/track_caller_errors.stderr b/tests/ui/eii/track_caller_errors.stderr new file mode 100644 index 0000000000000..e096146b67830 --- /dev/null +++ b/tests/ui/eii/track_caller_errors.stderr @@ -0,0 +1,11 @@ +error: `#[decl1]` is not allowed to have `#[track_caller]` + --> $DIR/track_caller_errors.rs:10:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | #[decl1] +LL | fn impl1(x: u64) { + | ---------------- `#[decl1]` is not allowed to have `#[track_caller]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/track_caller_static_errors.rs b/tests/ui/eii/track_caller_static_errors.rs new file mode 100644 index 0000000000000..8bd12de5ad8c8 --- /dev/null +++ b/tests/ui/eii/track_caller_static_errors.rs @@ -0,0 +1,11 @@ +//@ ignore-apple +// `#[track_caller]` is only valid on functions, not on EII (foreign) statics. +#![feature(extern_item_impls)] + +#[track_caller] //~ ERROR `#[track_caller]` attribute cannot be used on foreign statics +#[eii(sfoo)] +static FOO: u64 = 42; + +fn main() { + println!("{FOO}"); +} diff --git a/tests/ui/eii/track_caller_static_errors.stderr b/tests/ui/eii/track_caller_static_errors.stderr new file mode 100644 index 0000000000000..6b85ace2b0409 --- /dev/null +++ b/tests/ui/eii/track_caller_static_errors.stderr @@ -0,0 +1,10 @@ +error: `#[track_caller]` attribute cannot be used on foreign statics + --> $DIR/track_caller_static_errors.rs:5:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ + | + = help: `#[track_caller]` can only be applied to functions + +error: aborting due to 1 previous error +