Skip to content

Commit 3982eb3

Browse files
committed
Auto merge of rust-lang#81360 - Aaron1011:trait-caller-loc, r=nagisa
Support forwarding caller location through trait object method call Since PR rust-lang#69251, the `#[track_caller]` attribute has been supported on traits. However, it only has an effect on direct (monomorphized) method calls. Calling a `#[track_caller]` method on a trait object will *not* propagate caller location information - instead, `Location::caller()` will return the location of the method definition. This PR forwards caller location information when `#[track_caller]` is present on the method definition in the trait. This is possible because `#[track_caller]` in this position is 'inherited' by any impls of that trait, so all implementations will have the same ABI. This PR does *not* change the behavior in the case where `#[track_caller]` is present only on the impl of a trait. While all implementations of the method might have an explicit `#[track_caller]`, we cannot know this at codegen time, since other crates may have impls of the trait. Therefore, we keep the current behavior of not forwarding the caller location, ensuring that all implementations of the trait will have the correct ABI. See the modified test for examples of how this works
2 parents a84d1b2 + 6fd6624 commit 3982eb3

File tree

5 files changed

+102
-15
lines changed

5 files changed

+102
-15
lines changed

compiler/rustc_middle/src/dep_graph/dep_node.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ pub type DepNode = rustc_query_system::dep_graph::DepNode<DepKind>;
285285
// required that their size stay the same, but we don't want to change
286286
// it inadvertently. This assert just ensures we're aware of any change.
287287
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
288-
static_assert_size!(DepNode, 17);
288+
static_assert_size!(DepNode, 18);
289289

290290
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
291291
static_assert_size!(DepNode, 24);

compiler/rustc_middle/src/query/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,10 @@ rustc_queries! {
916916
desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) }
917917
}
918918

919+
query should_inherit_track_caller(def_id: DefId) -> bool {
920+
desc { |tcx| "computing should_inherit_track_caller of `{}`", tcx.def_path_str(def_id) }
921+
}
922+
919923
query lookup_deprecation_entry(def_id: DefId) -> Option<DeprecationEntry> {
920924
desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) }
921925
}

compiler/rustc_middle/src/ty/instance.rs

+48-4
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,9 @@ impl<'tcx> InstanceDef<'tcx> {
227227

228228
pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool {
229229
match *self {
230-
InstanceDef::Item(def) => {
231-
tcx.codegen_fn_attrs(def.did).flags.contains(CodegenFnAttrFlags::TRACK_CALLER)
230+
InstanceDef::Item(ty::WithOptConstParam { did: def_id, .. })
231+
| InstanceDef::Virtual(def_id, _) => {
232+
tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::TRACK_CALLER)
232233
}
233234
_ => false,
234235
}
@@ -403,7 +404,7 @@ impl<'tcx> Instance<'tcx> {
403404
def_id: DefId,
404405
substs: SubstsRef<'tcx>,
405406
) -> Option<Instance<'tcx>> {
406-
debug!("resolve(def_id={:?}, substs={:?})", def_id, substs);
407+
debug!("resolve_for_vtable(def_id={:?}, substs={:?})", def_id, substs);
407408
let fn_sig = tcx.fn_sig(def_id);
408409
let is_vtable_shim = !fn_sig.inputs().skip_binder().is_empty()
409410
&& fn_sig.input(0).skip_binder().is_param(0)
@@ -412,7 +413,50 @@ impl<'tcx> Instance<'tcx> {
412413
debug!(" => associated item with unsizeable self: Self");
413414
Some(Instance { def: InstanceDef::VtableShim(def_id), substs })
414415
} else {
415-
Instance::resolve_for_fn_ptr(tcx, param_env, def_id, substs)
416+
Instance::resolve(tcx, param_env, def_id, substs).ok().flatten().map(|mut resolved| {
417+
match resolved.def {
418+
InstanceDef::Item(def) => {
419+
// We need to generate a shim when we cannot guarantee that
420+
// the caller of a trait object method will be aware of
421+
// `#[track_caller]` - this ensures that the caller
422+
// and callee ABI will always match.
423+
//
424+
// The shim is generated when all of these conditions are met:
425+
//
426+
// 1) The underlying method expects a caller location parameter
427+
// in the ABI
428+
if resolved.def.requires_caller_location(tcx)
429+
// 2) The caller location parameter comes from having `#[track_caller]`
430+
// on the implementation, and *not* on the trait method.
431+
&& !tcx.should_inherit_track_caller(def.did)
432+
// If the method implementation comes from the trait definition itself
433+
// (e.g. `trait Foo { #[track_caller] my_fn() { /* impl */ } }`),
434+
// then we don't need to generate a shim. This check is needed because
435+
// `should_inherit_track_caller` returns `false` if our method
436+
// implementation comes from the trait block, and not an impl block
437+
&& !matches!(
438+
tcx.opt_associated_item(def.did),
439+
Some(ty::AssocItem {
440+
container: ty::AssocItemContainer::TraitContainer(_),
441+
..
442+
})
443+
)
444+
{
445+
debug!(
446+
" => vtable fn pointer created for function with #[track_caller]"
447+
);
448+
resolved.def = InstanceDef::ReifyShim(def.did);
449+
}
450+
}
451+
InstanceDef::Virtual(def_id, _) => {
452+
debug!(" => vtable fn pointer created for virtual call");
453+
resolved.def = InstanceDef::ReifyShim(def_id);
454+
}
455+
_ => {}
456+
}
457+
458+
resolved
459+
})
416460
}
417461
}
418462

compiler/rustc_typeck/src/collect.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ pub fn provide(providers: &mut Providers) {
9292
generator_kind,
9393
codegen_fn_attrs,
9494
collect_mod_item_types,
95+
should_inherit_track_caller,
9596
..*providers
9697
};
9798
}
@@ -2686,7 +2687,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
26862687
let attrs = tcx.get_attrs(id);
26872688

26882689
let mut codegen_fn_attrs = CodegenFnAttrs::new();
2689-
if should_inherit_track_caller(tcx, id) {
2690+
if tcx.should_inherit_track_caller(id) {
26902691
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
26912692
}
26922693

src/test/ui/rfc-2091-track-caller/tracked-trait-obj.rs

+47-9
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,60 @@
22

33
trait Tracked {
44
#[track_caller]
5-
fn handle(&self) {
5+
fn track_caller_trait_method(&self, line: u32, col: u32) {
66
let location = std::panic::Location::caller();
77
assert_eq!(location.file(), file!());
8-
// we only call this via trait object, so the def site should *always* be returned
9-
assert_eq!(location.line(), line!() - 4);
10-
assert_eq!(location.column(), 5);
8+
// The trait method definition is annotated with `#[track_caller]`,
9+
// so caller location information will work through a method
10+
// call on a trait object
11+
assert_eq!(location.line(), line, "Bad line");
12+
assert_eq!(location.column(), col, "Bad col");
1113
}
14+
15+
fn track_caller_not_on_trait_method(&self);
16+
17+
#[track_caller]
18+
fn track_caller_through_self(self: Box<Self>, line: u32, col: u32);
1219
}
1320

14-
impl Tracked for () {}
15-
impl Tracked for u8 {}
21+
impl Tracked for () {
22+
// We have `#[track_caller]` on the implementation of the method,
23+
// but not on the definition of the method in the trait. Therefore,
24+
// caller location information will *not* work through a method call
25+
// on a trait object. Instead, we will get the location of this method
26+
#[track_caller]
27+
fn track_caller_not_on_trait_method(&self) {
28+
let location = std::panic::Location::caller();
29+
assert_eq!(location.file(), file!());
30+
assert_eq!(location.line(), line!() - 3);
31+
assert_eq!(location.column(), 5);
32+
}
33+
34+
// We don't have a `#[track_caller]` attribute, but
35+
// `#[track_caller]` is present on the trait definition,
36+
// so we'll still get location information
37+
fn track_caller_through_self(self: Box<Self>, line: u32, col: u32) {
38+
let location = std::panic::Location::caller();
39+
assert_eq!(location.file(), file!());
40+
// The trait method definition is annotated with `#[track_caller]`,
41+
// so caller location information will work through a method
42+
// call on a trait object
43+
assert_eq!(location.line(), line, "Bad line");
44+
assert_eq!(location.column(), col, "Bad col");
45+
}
46+
}
1647

1748
fn main() {
18-
let tracked: &dyn Tracked = &5u8;
19-
tracked.handle();
49+
let tracked: &dyn Tracked = &();
50+
// The column is the start of 'track_caller_trait_method'
51+
tracked.track_caller_trait_method(line!(), 13);
2052

2153
const TRACKED: &dyn Tracked = &();
22-
TRACKED.handle();
54+
// The column is the start of 'track_caller_trait_method'
55+
TRACKED.track_caller_trait_method(line!(), 13);
56+
TRACKED.track_caller_not_on_trait_method();
57+
58+
// The column is the start of `track_caller_through_self`
59+
let boxed: Box<dyn Tracked> = Box::new(());
60+
boxed.track_caller_through_self(line!(), 11);
2361
}

0 commit comments

Comments
 (0)