Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions compiler/rustc_builtin_macros/src/eii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//@ no-prefer-dynamic
#![crate_type = "rlib"]
#![feature(extern_item_impls)]

#[track_caller]
#[eii(tcross)]
pub fn tcross(x: u64);
29 changes: 29 additions & 0 deletions tests/ui/eii/linking/track_caller_cross_crate.rs
Original file line number Diff line number Diff line change
@@ -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);
}
2 changes: 2 additions & 0 deletions tests/ui/eii/linking/track_caller_cross_crate.run.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
impl 1
impl 2
36 changes: 36 additions & 0 deletions tests/ui/eii/track_caller.rs
Original file line number Diff line number Diff line change
@@ -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);
}
18 changes: 18 additions & 0 deletions tests/ui/eii/track_caller_errors.rs
Original file line number Diff line number Diff line change
@@ -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);
}
11 changes: 11 additions & 0 deletions tests/ui/eii/track_caller_errors.stderr
Original file line number Diff line number Diff line change
@@ -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

11 changes: 11 additions & 0 deletions tests/ui/eii/track_caller_static_errors.rs
Original file line number Diff line number Diff line change
@@ -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}");
}
10 changes: 10 additions & 0 deletions tests/ui/eii/track_caller_static_errors.stderr
Original file line number Diff line number Diff line change
@@ -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

Loading