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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 48 additions & 4 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 47 additions & 9 deletions
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)