Skip to content

Commit

Permalink
Auto merge of #65881 - anp:implicit-caller-location, r=eddyb,oli-obk
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
bors committed Dec 7, 2019
2 parents 5c5c8eb + 15d1f7c commit de17464
Show file tree
Hide file tree
Showing 35 changed files with 286 additions and 141 deletions.
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
#![feature(staged_api)]
#![feature(std_internals)]
#![feature(stmt_expr_attributes)]
#![cfg_attr(not(bootstrap), feature(track_caller))]
#![feature(transparent_unions)]
#![feature(unboxed_closures)]
#![feature(unsized_locals)]
Expand Down
54 changes: 54 additions & 0 deletions src/libcore/panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,60 @@ pub struct Location<'a> {
col: u32,
}

impl<'a> Location<'a> {
/// Returns the source location of the caller of this function. If that function's caller is
/// annotated then its call location will be returned, and so on up the stack to the first call
/// within a non-tracked function body.
///
/// # Examples
///
/// ```
/// #![feature(track_caller)]
/// use core::panic::Location;
///
/// /// Returns the [`Location`] at which it is called.
/// #[track_caller]
/// fn get_caller_location() -> &'static Location<'static> {
/// Location::caller()
/// }
///
/// /// Returns a [`Location`] from within this function's definition.
/// fn get_just_one_location() -> &'static Location<'static> {
/// get_caller_location()
/// }
///
/// let fixed_location = get_just_one_location();
/// assert_eq!(fixed_location.file(), file!());
/// assert_eq!(fixed_location.line(), 15);
/// assert_eq!(fixed_location.column(), 5);
///
/// // running the same untracked function in a different location gives us the same result
/// let second_fixed_location = get_just_one_location();
/// assert_eq!(fixed_location.file(), second_fixed_location.file());
/// assert_eq!(fixed_location.line(), second_fixed_location.line());
/// assert_eq!(fixed_location.column(), second_fixed_location.column());
///
/// let this_location = get_caller_location();
/// assert_eq!(this_location.file(), file!());
/// assert_eq!(this_location.line(), 29);
/// assert_eq!(this_location.column(), 21);
///
/// // running the tracked function in a different location produces a different value
/// let another_location = get_caller_location();
/// assert_eq!(this_location.file(), another_location.file());
/// assert_ne!(this_location.line(), another_location.line());
/// assert_ne!(this_location.column(), another_location.column());
/// ```
#[cfg(not(bootstrap))]
#[unstable(feature = "track_caller",
reason = "uses #[track_caller] which is not yet stable",
issue = "47809")]
#[track_caller]
pub const fn caller() -> &'static Location<'static> {
crate::intrinsics::caller_location()
}
}

impl<'a> Location<'a> {
#![unstable(feature = "panic_internals",
reason = "internal details of the implementation of the `panic!` \
Expand Down
10 changes: 10 additions & 0 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::session::Session;
use crate::session::config::{BorrowckMode, OutputFilenames};
use crate::session::config::CrateType;
use crate::middle;
use crate::middle::lang_items::PanicLocationLangItem;
use crate::hir::{self, TraitCandidate, HirId, ItemKind, ItemLocalId, Node};
use crate::hir::def::{Res, DefKind, Export};
use crate::hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE};
Expand Down Expand Up @@ -1588,6 +1589,15 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn has_strict_asm_symbol_naming(&self) -> bool {
self.sess.target.target.arch.contains("nvptx")
}

/// Returns `&'static core::panic::Location<'static>`.
pub fn caller_location_ty(&self) -> Ty<'tcx> {
self.mk_imm_ref(
self.lifetimes.re_static,
self.type_of(self.require_lang_item(PanicLocationLangItem, None))
.subst(*self, self.mk_substs([self.lifetimes.re_static.into()].iter())),
)
}
}

impl<'tcx> GlobalCtxt<'tcx> {
Expand Down
9 changes: 5 additions & 4 deletions src/librustc/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ impl<'tcx> InstanceDef<'tcx> {
}
tcx.codegen_fn_attrs(self.def_id()).requests_inline()
}

pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool {
tcx.codegen_fn_attrs(self.def_id()).flags.contains(CodegenFnAttrFlags::TRACK_CALLER)
}
}

impl<'tcx> fmt::Display for Instance<'tcx> {
Expand Down Expand Up @@ -255,11 +259,8 @@ impl<'tcx> Instance<'tcx> {
) -> Option<Instance<'tcx>> {
debug!("resolve(def_id={:?}, substs={:?})", def_id, substs);
Instance::resolve(tcx, param_env, def_id, substs).map(|mut resolved| {
let has_track_caller = |def| tcx.codegen_fn_attrs(def).flags
.contains(CodegenFnAttrFlags::TRACK_CALLER);

match resolved.def {
InstanceDef::Item(def_id) if has_track_caller(def_id) => {
InstanceDef::Item(def_id) if resolved.def.requires_caller_location(tcx) => {
debug!(" => fn pointer created for function with #[track_caller]");
resolved.def = InstanceDef::ReifyShim(def_id);
}
Expand Down
13 changes: 11 additions & 2 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2434,6 +2434,7 @@ where
cx: &C,
sig: ty::PolyFnSig<'tcx>,
extra_args: &[Ty<'tcx>],
caller_location: Option<Ty<'tcx>>,
mk_arg_type: impl Fn(Ty<'tcx>, Option<usize>) -> ArgAbi<'tcx, Ty<'tcx>>,
) -> Self;
fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi);
Expand All @@ -2448,13 +2449,19 @@ where
+ HasParamEnv<'tcx>,
{
fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
call::FnAbi::new_internal(cx, sig, extra_args, |ty, _| ArgAbi::new(cx.layout_of(ty)))
call::FnAbi::new_internal(cx, sig, extra_args, None, |ty, _| ArgAbi::new(cx.layout_of(ty)))
}

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

call::FnAbi::new_internal(cx, sig, extra_args, |ty, arg_idx| {
let caller_location = if instance.def.requires_caller_location(cx.tcx()) {
Some(cx.tcx().caller_location_ty())
} else {
None
};

call::FnAbi::new_internal(cx, sig, extra_args, caller_location, |ty, arg_idx| {
let mut layout = cx.layout_of(ty);
// Don't pass the vtable, it's not an argument of the virtual fn.
// Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait`
Expand Down Expand Up @@ -2512,6 +2519,7 @@ where
cx: &C,
sig: ty::PolyFnSig<'tcx>,
extra_args: &[Ty<'tcx>],
caller_location: Option<Ty<'tcx>>,
mk_arg_type: impl Fn(Ty<'tcx>, Option<usize>) -> ArgAbi<'tcx, Ty<'tcx>>,
) -> Self {
debug!("FnAbi::new_internal({:?}, {:?})", sig, extra_args);
Expand Down Expand Up @@ -2684,6 +2692,7 @@ where
.iter()
.cloned()
.chain(extra_args)
.chain(caller_location)
.enumerate()
.map(|(i, ty)| arg_of(ty, Some(i)))
.collect(),
Expand Down
30 changes: 22 additions & 8 deletions src/librustc_codegen_ssa/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
&fn_abi.args[first_args.len()..])
}

let needs_location =
instance.map_or(false, |i| i.def.requires_caller_location(self.cx.tcx()));
if needs_location {
assert_eq!(
fn_abi.args.len(), args.len() + 1,
"#[track_caller] fn's must have 1 more argument in their ABI than in their MIR",
);
let location = self.get_caller_location(&mut bx, span);
let last_arg = fn_abi.args.last().unwrap();
self.codegen_argument(&mut bx, location, &mut llargs, last_arg);
}

let fn_ptr = match (llfn, instance) {
(Some(llfn), _) => llfn,
(None, Some(instance)) => bx.get_fn_addr(instance),
Expand Down Expand Up @@ -1010,14 +1022,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx: &mut Bx,
span: Span,
) -> OperandRef<'tcx, Bx::Value> {
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
let caller = bx.tcx().sess.source_map().lookup_char_pos(topmost.lo());
let const_loc = bx.tcx().const_caller_location((
Symbol::intern(&caller.file.name.to_string()),
caller.line as u32,
caller.col_display as u32 + 1,
));
OperandRef::from_const(bx, const_loc)
self.caller_location.unwrap_or_else(|| {
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
let caller = bx.tcx().sess.source_map().lookup_char_pos(topmost.lo());
let const_loc = bx.tcx().const_caller_location((
Symbol::intern(&caller.file.name.to_string()),
caller.line as u32,
caller.col_display as u32 + 1,
));
OperandRef::from_const(bx, const_loc)
})
}

fn get_personality_slot(
Expand Down
32 changes: 28 additions & 4 deletions src/librustc_codegen_ssa/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
/// All `VarDebuginfo` from the MIR body, partitioned by `Local`.
/// This is `None` if no variable debuginfo/names are needed.
per_local_var_debug_info: Option<IndexVec<mir::Local, Vec<&'tcx mir::VarDebugInfo<'tcx>>>>,

/// Caller location propagated if this function has `#[track_caller]`.
caller_location: Option<OperandRef<'tcx, Bx::Value>>,
}

impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
Expand Down Expand Up @@ -172,13 +175,14 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
locals: IndexVec::new(),
debug_context,
per_local_var_debug_info: debuginfo::per_local_var_debug_info(cx.tcx(), mir_body),
caller_location: None,
};

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

// Allocate variable and temp allocas
fx.locals = {
let args = arg_local_refs(&mut bx, &fx, &memory_locals);
let args = arg_local_refs(&mut bx, &mut fx, &memory_locals);

let mut allocate_local = |local| {
let decl = &mir_body.local_decls[local];
Expand Down Expand Up @@ -320,14 +324,14 @@ fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
/// indirect.
fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
fx: &FunctionCx<'a, 'tcx, Bx>,
fx: &mut FunctionCx<'a, 'tcx, Bx>,
memory_locals: &BitSet<mir::Local>,
) -> Vec<LocalRef<'tcx, Bx::Value>> {
let mir = fx.mir;
let mut idx = 0;
let mut llarg_idx = fx.fn_abi.ret.is_indirect() as usize;

mir.args_iter().enumerate().map(|(arg_index, local)| {
let args = mir.args_iter().enumerate().map(|(arg_index, local)| {
let arg_decl = &mir.local_decls[local];

if Some(local) == mir.spread_arg {
Expand Down Expand Up @@ -423,7 +427,27 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx.store_fn_arg(arg, &mut llarg_idx, tmp);
LocalRef::Place(tmp)
}
}).collect()
}).collect::<Vec<_>>();

if fx.instance.def.requires_caller_location(bx.tcx()) {
assert_eq!(
fx.fn_abi.args.len(), args.len() + 1,
"#[track_caller] fn's must have 1 more argument in their ABI than in their MIR",
);

let arg = fx.fn_abi.args.last().unwrap();
match arg.mode {
PassMode::Direct(_) => (),
_ => bug!("caller location must be PassMode::Direct, found {:?}", arg.mode),
}

fx.caller_location = Some(OperandRef {
val: OperandValue::Immediate(bx.get_param(llarg_idx)),
layout: arg.layout,
});
}

args
}

mod analyze;
Expand Down
1 change: 0 additions & 1 deletion src/librustc_feature/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,5 +541,4 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
sym::or_patterns,
sym::let_chains,
sym::raw_dylib,
sym::track_caller,
];
15 changes: 7 additions & 8 deletions src/librustc_mir/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ use std::convert::TryInto;

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

let loc_ty = tcx.mk_imm_ref(
tcx.lifetimes.re_static,
tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
.subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
);
let loc_ty = tcx.caller_location_ty();
let loc_place = ecx.alloc_caller_location(file, line, col);
intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap();
let loc_const = ty::Const {
Expand Down
1 change: 1 addition & 0 deletions src/librustc_mir/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// `src/librustc/ty/constness.rs`
match intrinsic_name {
sym::caller_location => {
let span = self.find_closest_untracked_caller_location().unwrap_or(span);
let location = self.alloc_caller_location_for_span(span);
self.write_scalar(location.ptr, dest)?;
}
Expand Down
15 changes: 15 additions & 0 deletions src/librustc_mir/interpret/intrinsics/caller_location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ use syntax_pos::{Symbol, Span};
use crate::interpret::{Scalar, MemoryKind, MPlaceTy, intrinsics::{InterpCx, Machine}};

impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Walks up the callstack from the intrinsic's callsite, searching for the first frame which is
/// not `#[track_caller]`.
crate fn find_closest_untracked_caller_location(&self) -> Option<Span> {
let mut caller_span = None;
for next_caller in self.stack.iter().rev() {
if !next_caller.instance.def.requires_caller_location(*self.tcx) {
return caller_span;
}
caller_span = Some(next_caller.span);
}

caller_span
}

/// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.
crate fn alloc_caller_location(
&mut self,
filename: Symbol,
Expand Down
5 changes: 5 additions & 0 deletions src/librustc_mir/transform/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ impl Inliner<'tcx> {

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

if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::TRACK_CALLER) {
debug!("`#[track_caller]` present - not inlining");
return false;
}

let hinted = match codegen_fn_attrs.inline {
// Just treat inline(always) as a hint for now,
// there are cases that prevent inlining that we
Expand Down
11 changes: 1 addition & 10 deletions src/librustc_typeck/check/intrinsic.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Type-checking for the rust-intrinsic and platform-intrinsic
//! intrinsics that the compiler exposes.
use rustc::middle::lang_items::PanicLocationLangItem;
use rustc::traits::{ObligationCause, ObligationCauseCode};
use rustc::ty::{self, TyCtxt, Ty};
use rustc::ty::subst::Subst;
Expand Down Expand Up @@ -148,15 +147,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem) {
], tcx.types.usize)
}
"rustc_peek" => (1, vec![param(0)], param(0)),
"caller_location" => (
0,
vec![],
tcx.mk_imm_ref(
tcx.lifetimes.re_static,
tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
.subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
),
),
"caller_location" => (0, vec![], tcx.caller_location_ty()),
"panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()),
"init" => (1, Vec::new(), param(0)),
"uninit" => (1, Vec::new(), param(0)),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2616,7 +2616,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
tcx.sess,
attr.span,
E0737,
"Rust ABI is required to use `#[track_caller]`"
"`#[track_caller]` requires Rust ABI"
).emit();
}
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
Expand Down
Loading

0 comments on commit de17464

Please sign in to comment.