Skip to content

Commit de17464

Browse files
committed
Auto merge of #65881 - anp:implicit-caller-location, r=eddyb,oli-obk
Implement #[track_caller] attribute. (RFC 2091 4/N) Implements the `#[track_caller]` attribute in both const and codegen contexts. The const implementation walks up the stack to find the nearest untracked callsite. The codegen implementation adds an implicit argument to tracked function calls, and populates it with either a call to the previously-landed intrinsic or if the caller has `#[track_caller]` with a copy of the location passed to the current function. Also includes a little cleanup and a few comments in the other caller location areas. [Depends on: 65664](#65664) [RFC 2091 text](https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md) [Tracking issue](#47809) [Tracking doc](https://paper.dropbox.com/doc/track_rfc_2091_impl-notes--Anf1NwnIb0xcRv31YLIadyj0Ag-rwCdRc2fi2yvRZ7syGZ9q#:uid=863513134494965680023183&h2=TODO-actually-pass-location-to)
2 parents 5c5c8eb + 15d1f7c commit de17464

35 files changed

+286
-141
lines changed

src/libcore/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
#![feature(staged_api)]
103103
#![feature(std_internals)]
104104
#![feature(stmt_expr_attributes)]
105+
#![cfg_attr(not(bootstrap), feature(track_caller))]
105106
#![feature(transparent_unions)]
106107
#![feature(unboxed_closures)]
107108
#![feature(unsized_locals)]

src/libcore/panic.rs

+54
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,60 @@ pub struct Location<'a> {
176176
col: u32,
177177
}
178178

179+
impl<'a> Location<'a> {
180+
/// Returns the source location of the caller of this function. If that function's caller is
181+
/// annotated then its call location will be returned, and so on up the stack to the first call
182+
/// within a non-tracked function body.
183+
///
184+
/// # Examples
185+
///
186+
/// ```
187+
/// #![feature(track_caller)]
188+
/// use core::panic::Location;
189+
///
190+
/// /// Returns the [`Location`] at which it is called.
191+
/// #[track_caller]
192+
/// fn get_caller_location() -> &'static Location<'static> {
193+
/// Location::caller()
194+
/// }
195+
///
196+
/// /// Returns a [`Location`] from within this function's definition.
197+
/// fn get_just_one_location() -> &'static Location<'static> {
198+
/// get_caller_location()
199+
/// }
200+
///
201+
/// let fixed_location = get_just_one_location();
202+
/// assert_eq!(fixed_location.file(), file!());
203+
/// assert_eq!(fixed_location.line(), 15);
204+
/// assert_eq!(fixed_location.column(), 5);
205+
///
206+
/// // running the same untracked function in a different location gives us the same result
207+
/// let second_fixed_location = get_just_one_location();
208+
/// assert_eq!(fixed_location.file(), second_fixed_location.file());
209+
/// assert_eq!(fixed_location.line(), second_fixed_location.line());
210+
/// assert_eq!(fixed_location.column(), second_fixed_location.column());
211+
///
212+
/// let this_location = get_caller_location();
213+
/// assert_eq!(this_location.file(), file!());
214+
/// assert_eq!(this_location.line(), 29);
215+
/// assert_eq!(this_location.column(), 21);
216+
///
217+
/// // running the tracked function in a different location produces a different value
218+
/// let another_location = get_caller_location();
219+
/// assert_eq!(this_location.file(), another_location.file());
220+
/// assert_ne!(this_location.line(), another_location.line());
221+
/// assert_ne!(this_location.column(), another_location.column());
222+
/// ```
223+
#[cfg(not(bootstrap))]
224+
#[unstable(feature = "track_caller",
225+
reason = "uses #[track_caller] which is not yet stable",
226+
issue = "47809")]
227+
#[track_caller]
228+
pub const fn caller() -> &'static Location<'static> {
229+
crate::intrinsics::caller_location()
230+
}
231+
}
232+
179233
impl<'a> Location<'a> {
180234
#![unstable(feature = "panic_internals",
181235
reason = "internal details of the implementation of the `panic!` \

src/librustc/ty/context.rs

+10
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::session::Session;
88
use crate::session::config::{BorrowckMode, OutputFilenames};
99
use crate::session::config::CrateType;
1010
use crate::middle;
11+
use crate::middle::lang_items::PanicLocationLangItem;
1112
use crate::hir::{self, TraitCandidate, HirId, ItemKind, ItemLocalId, Node};
1213
use crate::hir::def::{Res, DefKind, Export};
1314
use crate::hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE};
@@ -1588,6 +1589,15 @@ impl<'tcx> TyCtxt<'tcx> {
15881589
pub fn has_strict_asm_symbol_naming(&self) -> bool {
15891590
self.sess.target.target.arch.contains("nvptx")
15901591
}
1592+
1593+
/// Returns `&'static core::panic::Location<'static>`.
1594+
pub fn caller_location_ty(&self) -> Ty<'tcx> {
1595+
self.mk_imm_ref(
1596+
self.lifetimes.re_static,
1597+
self.type_of(self.require_lang_item(PanicLocationLangItem, None))
1598+
.subst(*self, self.mk_substs([self.lifetimes.re_static.into()].iter())),
1599+
)
1600+
}
15911601
}
15921602

15931603
impl<'tcx> GlobalCtxt<'tcx> {

src/librustc/ty/instance.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ impl<'tcx> InstanceDef<'tcx> {
116116
}
117117
tcx.codegen_fn_attrs(self.def_id()).requests_inline()
118118
}
119+
120+
pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool {
121+
tcx.codegen_fn_attrs(self.def_id()).flags.contains(CodegenFnAttrFlags::TRACK_CALLER)
122+
}
119123
}
120124

121125
impl<'tcx> fmt::Display for Instance<'tcx> {
@@ -255,11 +259,8 @@ impl<'tcx> Instance<'tcx> {
255259
) -> Option<Instance<'tcx>> {
256260
debug!("resolve(def_id={:?}, substs={:?})", def_id, substs);
257261
Instance::resolve(tcx, param_env, def_id, substs).map(|mut resolved| {
258-
let has_track_caller = |def| tcx.codegen_fn_attrs(def).flags
259-
.contains(CodegenFnAttrFlags::TRACK_CALLER);
260-
261262
match resolved.def {
262-
InstanceDef::Item(def_id) if has_track_caller(def_id) => {
263+
InstanceDef::Item(def_id) if resolved.def.requires_caller_location(tcx) => {
263264
debug!(" => fn pointer created for function with #[track_caller]");
264265
resolved.def = InstanceDef::ReifyShim(def_id);
265266
}

src/librustc/ty/layout.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -2434,6 +2434,7 @@ where
24342434
cx: &C,
24352435
sig: ty::PolyFnSig<'tcx>,
24362436
extra_args: &[Ty<'tcx>],
2437+
caller_location: Option<Ty<'tcx>>,
24372438
mk_arg_type: impl Fn(Ty<'tcx>, Option<usize>) -> ArgAbi<'tcx, Ty<'tcx>>,
24382439
) -> Self;
24392440
fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi);
@@ -2448,13 +2449,19 @@ where
24482449
+ HasParamEnv<'tcx>,
24492450
{
24502451
fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
2451-
call::FnAbi::new_internal(cx, sig, extra_args, |ty, _| ArgAbi::new(cx.layout_of(ty)))
2452+
call::FnAbi::new_internal(cx, sig, extra_args, None, |ty, _| ArgAbi::new(cx.layout_of(ty)))
24522453
}
24532454

24542455
fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
24552456
let sig = instance.fn_sig_for_fn_abi(cx.tcx());
24562457

2457-
call::FnAbi::new_internal(cx, sig, extra_args, |ty, arg_idx| {
2458+
let caller_location = if instance.def.requires_caller_location(cx.tcx()) {
2459+
Some(cx.tcx().caller_location_ty())
2460+
} else {
2461+
None
2462+
};
2463+
2464+
call::FnAbi::new_internal(cx, sig, extra_args, caller_location, |ty, arg_idx| {
24582465
let mut layout = cx.layout_of(ty);
24592466
// Don't pass the vtable, it's not an argument of the virtual fn.
24602467
// Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait`
@@ -2512,6 +2519,7 @@ where
25122519
cx: &C,
25132520
sig: ty::PolyFnSig<'tcx>,
25142521
extra_args: &[Ty<'tcx>],
2522+
caller_location: Option<Ty<'tcx>>,
25152523
mk_arg_type: impl Fn(Ty<'tcx>, Option<usize>) -> ArgAbi<'tcx, Ty<'tcx>>,
25162524
) -> Self {
25172525
debug!("FnAbi::new_internal({:?}, {:?})", sig, extra_args);
@@ -2684,6 +2692,7 @@ where
26842692
.iter()
26852693
.cloned()
26862694
.chain(extra_args)
2695+
.chain(caller_location)
26872696
.enumerate()
26882697
.map(|(i, ty)| arg_of(ty, Some(i)))
26892698
.collect(),

src/librustc_codegen_ssa/mir/block.rs

+22-8
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
774774
&fn_abi.args[first_args.len()..])
775775
}
776776

777+
let needs_location =
778+
instance.map_or(false, |i| i.def.requires_caller_location(self.cx.tcx()));
779+
if needs_location {
780+
assert_eq!(
781+
fn_abi.args.len(), args.len() + 1,
782+
"#[track_caller] fn's must have 1 more argument in their ABI than in their MIR",
783+
);
784+
let location = self.get_caller_location(&mut bx, span);
785+
let last_arg = fn_abi.args.last().unwrap();
786+
self.codegen_argument(&mut bx, location, &mut llargs, last_arg);
787+
}
788+
777789
let fn_ptr = match (llfn, instance) {
778790
(Some(llfn), _) => llfn,
779791
(None, Some(instance)) => bx.get_fn_addr(instance),
@@ -1010,14 +1022,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10101022
bx: &mut Bx,
10111023
span: Span,
10121024
) -> OperandRef<'tcx, Bx::Value> {
1013-
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
1014-
let caller = bx.tcx().sess.source_map().lookup_char_pos(topmost.lo());
1015-
let const_loc = bx.tcx().const_caller_location((
1016-
Symbol::intern(&caller.file.name.to_string()),
1017-
caller.line as u32,
1018-
caller.col_display as u32 + 1,
1019-
));
1020-
OperandRef::from_const(bx, const_loc)
1025+
self.caller_location.unwrap_or_else(|| {
1026+
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
1027+
let caller = bx.tcx().sess.source_map().lookup_char_pos(topmost.lo());
1028+
let const_loc = bx.tcx().const_caller_location((
1029+
Symbol::intern(&caller.file.name.to_string()),
1030+
caller.line as u32,
1031+
caller.col_display as u32 + 1,
1032+
));
1033+
OperandRef::from_const(bx, const_loc)
1034+
})
10211035
}
10221036

10231037
fn get_personality_slot(

src/librustc_codegen_ssa/mir/mod.rs

+28-4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
7777
/// All `VarDebuginfo` from the MIR body, partitioned by `Local`.
7878
/// This is `None` if no variable debuginfo/names are needed.
7979
per_local_var_debug_info: Option<IndexVec<mir::Local, Vec<&'tcx mir::VarDebugInfo<'tcx>>>>,
80+
81+
/// Caller location propagated if this function has `#[track_caller]`.
82+
caller_location: Option<OperandRef<'tcx, Bx::Value>>,
8083
}
8184

8285
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
@@ -172,13 +175,14 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
172175
locals: IndexVec::new(),
173176
debug_context,
174177
per_local_var_debug_info: debuginfo::per_local_var_debug_info(cx.tcx(), mir_body),
178+
caller_location: None,
175179
};
176180

177181
let memory_locals = analyze::non_ssa_locals(&fx);
178182

179183
// Allocate variable and temp allocas
180184
fx.locals = {
181-
let args = arg_local_refs(&mut bx, &fx, &memory_locals);
185+
let args = arg_local_refs(&mut bx, &mut fx, &memory_locals);
182186

183187
let mut allocate_local = |local| {
184188
let decl = &mir_body.local_decls[local];
@@ -320,14 +324,14 @@ fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
320324
/// indirect.
321325
fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
322326
bx: &mut Bx,
323-
fx: &FunctionCx<'a, 'tcx, Bx>,
327+
fx: &mut FunctionCx<'a, 'tcx, Bx>,
324328
memory_locals: &BitSet<mir::Local>,
325329
) -> Vec<LocalRef<'tcx, Bx::Value>> {
326330
let mir = fx.mir;
327331
let mut idx = 0;
328332
let mut llarg_idx = fx.fn_abi.ret.is_indirect() as usize;
329333

330-
mir.args_iter().enumerate().map(|(arg_index, local)| {
334+
let args = mir.args_iter().enumerate().map(|(arg_index, local)| {
331335
let arg_decl = &mir.local_decls[local];
332336

333337
if Some(local) == mir.spread_arg {
@@ -423,7 +427,27 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
423427
bx.store_fn_arg(arg, &mut llarg_idx, tmp);
424428
LocalRef::Place(tmp)
425429
}
426-
}).collect()
430+
}).collect::<Vec<_>>();
431+
432+
if fx.instance.def.requires_caller_location(bx.tcx()) {
433+
assert_eq!(
434+
fx.fn_abi.args.len(), args.len() + 1,
435+
"#[track_caller] fn's must have 1 more argument in their ABI than in their MIR",
436+
);
437+
438+
let arg = fx.fn_abi.args.last().unwrap();
439+
match arg.mode {
440+
PassMode::Direct(_) => (),
441+
_ => bug!("caller location must be PassMode::Direct, found {:?}", arg.mode),
442+
}
443+
444+
fx.caller_location = Some(OperandRef {
445+
val: OperandValue::Immediate(bx.get_param(llarg_idx)),
446+
layout: arg.layout,
447+
});
448+
}
449+
450+
args
427451
}
428452

429453
mod analyze;

src/librustc_feature/active.rs

-1
Original file line numberDiff line numberDiff line change
@@ -541,5 +541,4 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
541541
sym::or_patterns,
542542
sym::let_chains,
543543
sym::raw_dylib,
544-
sym::track_caller,
545544
];

src/librustc_mir/const_eval.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ use std::convert::TryInto;
99

1010
use rustc::hir::def::DefKind;
1111
use rustc::hir::def_id::DefId;
12-
use rustc::middle::lang_items::PanicLocationLangItem;
1312
use rustc::mir::interpret::{ConstEvalErr, ErrorHandled, ScalarMaybeUndef};
1413
use rustc::mir;
1514
use rustc::ty::{self, Ty, TyCtxt, subst::Subst};
16-
use rustc::ty::layout::{self, LayoutOf, VariantIdx};
15+
use rustc::ty::layout::{self, HasTyCtxt, LayoutOf, VariantIdx};
1716
use rustc::traits::Reveal;
1817
use rustc_data_structures::fx::FxHashMap;
1918
use crate::interpret::eval_nullary_intrinsic;
@@ -348,7 +347,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
348347
//
349348
// For the moment we only do this for functions which take no arguments
350349
// (or all arguments are ZSTs) so that we don't memoize too much.
351-
if args.iter().all(|a| a.layout.is_zst()) {
350+
//
351+
// Because `#[track_caller]` adds an implicit non-ZST argument, we also cannot
352+
// perform this optimization on items tagged with it.
353+
let no_implicit_args = !instance.def.requires_caller_location(ecx.tcx());
354+
if args.iter().all(|a| a.layout.is_zst()) && no_implicit_args {
352355
let gid = GlobalId { instance, promoted: None };
353356
ecx.eval_const_fn_call(gid, ret)?;
354357
return Ok(None);
@@ -559,11 +562,7 @@ pub fn const_caller_location<'tcx>(
559562
trace!("const_caller_location: {}:{}:{}", file, line, col);
560563
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all());
561564

562-
let loc_ty = tcx.mk_imm_ref(
563-
tcx.lifetimes.re_static,
564-
tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
565-
.subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
566-
);
565+
let loc_ty = tcx.caller_location_ty();
567566
let loc_place = ecx.alloc_caller_location(file, line, col);
568567
intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap();
569568
let loc_const = ty::Const {

src/librustc_mir/interpret/intrinsics.rs

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
112112
// `src/librustc/ty/constness.rs`
113113
match intrinsic_name {
114114
sym::caller_location => {
115+
let span = self.find_closest_untracked_caller_location().unwrap_or(span);
115116
let location = self.alloc_caller_location_for_span(span);
116117
self.write_scalar(location.ptr, dest)?;
117118
}

src/librustc_mir/interpret/intrinsics/caller_location.rs

+15
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@ use syntax_pos::{Symbol, Span};
66
use crate::interpret::{Scalar, MemoryKind, MPlaceTy, intrinsics::{InterpCx, Machine}};
77

88
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
9+
/// Walks up the callstack from the intrinsic's callsite, searching for the first frame which is
10+
/// not `#[track_caller]`.
11+
crate fn find_closest_untracked_caller_location(&self) -> Option<Span> {
12+
let mut caller_span = None;
13+
for next_caller in self.stack.iter().rev() {
14+
if !next_caller.instance.def.requires_caller_location(*self.tcx) {
15+
return caller_span;
16+
}
17+
caller_span = Some(next_caller.span);
18+
}
19+
20+
caller_span
21+
}
22+
23+
/// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.
924
crate fn alloc_caller_location(
1025
&mut self,
1126
filename: Symbol,

src/librustc_mir/transform/inline.rs

+5
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,11 @@ impl Inliner<'tcx> {
230230

231231
let codegen_fn_attrs = tcx.codegen_fn_attrs(callsite.callee);
232232

233+
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::TRACK_CALLER) {
234+
debug!("`#[track_caller]` present - not inlining");
235+
return false;
236+
}
237+
233238
let hinted = match codegen_fn_attrs.inline {
234239
// Just treat inline(always) as a hint for now,
235240
// there are cases that prevent inlining that we

src/librustc_typeck/check/intrinsic.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Type-checking for the rust-intrinsic and platform-intrinsic
22
//! intrinsics that the compiler exposes.
33
4-
use rustc::middle::lang_items::PanicLocationLangItem;
54
use rustc::traits::{ObligationCause, ObligationCauseCode};
65
use rustc::ty::{self, TyCtxt, Ty};
76
use rustc::ty::subst::Subst;
@@ -148,15 +147,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem) {
148147
], tcx.types.usize)
149148
}
150149
"rustc_peek" => (1, vec![param(0)], param(0)),
151-
"caller_location" => (
152-
0,
153-
vec![],
154-
tcx.mk_imm_ref(
155-
tcx.lifetimes.re_static,
156-
tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
157-
.subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
158-
),
159-
),
150+
"caller_location" => (0, vec![], tcx.caller_location_ty()),
160151
"panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()),
161152
"init" => (1, Vec::new(), param(0)),
162153
"uninit" => (1, Vec::new(), param(0)),

src/librustc_typeck/collect.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2616,7 +2616,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
26162616
tcx.sess,
26172617
attr.span,
26182618
E0737,
2619-
"Rust ABI is required to use `#[track_caller]`"
2619+
"`#[track_caller]` requires Rust ABI"
26202620
).emit();
26212621
}
26222622
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;

0 commit comments

Comments
 (0)