Skip to content

Implement #[track_caller] attribute. (RFC 2091 4/N) #65881

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Dec 8, 2019
Merged
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
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
@@ -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)]
54 changes: 54 additions & 0 deletions src/libcore/panic.rs
Original file line number Diff line number Diff line change
@@ -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!` \
10 changes: 10 additions & 0 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
@@ -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};
@@ -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> {
9 changes: 5 additions & 4 deletions src/librustc/ty/instance.rs
Original file line number Diff line number Diff line change
@@ -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> {
@@ -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);
}
13 changes: 11 additions & 2 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
@@ -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);
@@ -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`
@@ -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);
@@ -2684,6 +2692,7 @@ where
.iter()
.cloned()
.chain(extra_args)
.chain(caller_location)
.enumerate()
.map(|(i, ty)| arg_of(ty, Some(i)))
.collect(),
30 changes: 22 additions & 8 deletions src/librustc_codegen_ssa/mir/block.rs
Original file line number Diff line number Diff line change
@@ -770,6 +770,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),
@@ -1004,14 +1016,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(
32 changes: 28 additions & 4 deletions src/librustc_codegen_ssa/mir/mod.rs
Original file line number Diff line number Diff line change
@@ -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> {
@@ -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];
@@ -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 {
@@ -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;
1 change: 0 additions & 1 deletion src/librustc_feature/active.rs
Original file line number Diff line number Diff line change
@@ -538,5 +538,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
@@ -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;
@@ -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);
@@ -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 {
1 change: 1 addition & 0 deletions src/librustc_mir/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
@@ -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)?;
}
15 changes: 15 additions & 0 deletions src/librustc_mir/interpret/intrinsics/caller_location.rs
Original file line number Diff line number Diff line change
@@ -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,
5 changes: 5 additions & 0 deletions src/librustc_mir/transform/inline.rs
Original file line number Diff line number Diff line change
@@ -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
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;
@@ -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)),
2 changes: 1 addition & 1 deletion src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
@@ -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;
23 changes: 0 additions & 23 deletions src/test/ui/consts/const-eval/const_caller_location.rs

This file was deleted.

18 changes: 12 additions & 6 deletions src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
// run-pass

#![feature(core_intrinsics)]
#![feature(track_caller)]

#[inline(never)]
#[track_caller]
fn defeat_const_prop() -> &'static core::panic::Location<'static> {
core::panic::Location::caller()
}

macro_rules! caller_location_from_macro {
() => (core::intrinsics::caller_location());
() => (defeat_const_prop());
}

fn main() {
let loc = core::intrinsics::caller_location();
let loc = defeat_const_prop();
assert_eq!(loc.file(), file!());
assert_eq!(loc.line(), 10);
assert_eq!(loc.line(), 16);
assert_eq!(loc.column(), 15);

// `caller_location()` in a macro should behave similarly to `file!` and `line!`,
// `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
// i.e. point to where the macro was invoked, instead of the macro itself.
let loc2 = caller_location_from_macro!();
assert_eq!(loc2.file(), file!());
assert_eq!(loc2.line(), 17);
assert_eq!(loc2.line(), 23);
assert_eq!(loc2.column(), 16);
}
41 changes: 41 additions & 0 deletions src/test/ui/rfc-2091-track-caller/const-caller-location.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// run-pass

#![feature(const_fn, track_caller)]

use std::panic::Location;

const LOCATION: &Location = Location::caller();

const TRACKED: &Location = tracked();
#[track_caller]
const fn tracked() -> &'static Location <'static> {
Location::caller()
}

const NESTED: &Location = nested_location();
const fn nested_location() -> &'static Location<'static> {
Location::caller()
}

const CONTAINED: &Location = contained();
const fn contained() -> &'static Location<'static> {
tracked()
}

fn main() {
assert_eq!(LOCATION.file(), file!());
assert_eq!(LOCATION.line(), 7);
assert_eq!(LOCATION.column(), 29);

assert_eq!(TRACKED.file(), file!());
assert_eq!(TRACKED.line(), 9);
assert_eq!(TRACKED.column(), 28);

assert_eq!(NESTED.file(), file!());
assert_eq!(NESTED.line(), 17);
assert_eq!(NESTED.column(), 5);

assert_eq!(CONTAINED.file(), file!());
assert_eq!(CONTAINED.line(), 22);
assert_eq!(CONTAINED.column(), 5);
}
2 changes: 1 addition & 1 deletion src/test/ui/rfc-2091-track-caller/error-odd-syntax.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(track_caller)]

#[track_caller(1)]
fn f() {}
8 changes: 0 additions & 8 deletions src/test/ui/rfc-2091-track-caller/error-odd-syntax.stderr
Original file line number Diff line number Diff line change
@@ -4,13 +4,5 @@ error: malformed `track_caller` attribute input
LL | #[track_caller(1)]
| ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[track_caller]`

warning: the feature `track_caller` is incomplete and may cause the compiler to crash
--> $DIR/error-odd-syntax.rs:1:12
|
LL | #![feature(track_caller)]
| ^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

error: aborting due to previous error

5 changes: 3 additions & 2 deletions src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(track_caller)]

#[track_caller] //~ ERROR Rust ABI is required to use `#[track_caller]`
#[track_caller]
extern "C" fn f() {}
//~^^ ERROR `#[track_caller]` requires Rust ABI

fn main() {}
10 changes: 1 addition & 9 deletions src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
warning: the feature `track_caller` is incomplete and may cause the compiler to crash
--> $DIR/error-with-invalid-abi.rs:1:12
|
LL | #![feature(track_caller)]
| ^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

error[E0737]: Rust ABI is required to use `#[track_caller]`
error[E0737]: `#[track_caller]` requires Rust ABI
--> $DIR/error-with-invalid-abi.rs:3:1
|
LL | #[track_caller]
2 changes: 1 addition & 1 deletion src/test/ui/rfc-2091-track-caller/error-with-naked.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(naked_functions, track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(naked_functions, track_caller)]

#[track_caller]
#[naked]
8 changes: 0 additions & 8 deletions src/test/ui/rfc-2091-track-caller/error-with-naked.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
warning: the feature `track_caller` is incomplete and may cause the compiler to crash
--> $DIR/error-with-naked.rs:1:29
|
LL | #![feature(naked_functions, track_caller)]
| ^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

error[E0736]: cannot use `#[track_caller]` with `#[naked]`
--> $DIR/error-with-naked.rs:3:1
|
2 changes: 1 addition & 1 deletion src/test/ui/rfc-2091-track-caller/error-with-trait-decl.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(track_caller)]

trait Trait {
#[track_caller] //~ ERROR: `#[track_caller]` may not be used on trait methods
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
warning: the feature `track_caller` is incomplete and may cause the compiler to crash
--> $DIR/error-with-trait-decl.rs:1:12
|
LL | #![feature(track_caller)]
| ^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

error[E0738]: `#[track_caller]` may not be used on trait methods
--> $DIR/error-with-trait-decl.rs:4:5
|
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(track_caller)]

trait Trait {
#[track_caller] //~ ERROR: `#[track_caller]` may not be used on trait methods
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
warning: the feature `track_caller` is incomplete and may cause the compiler to crash
--> $DIR/error-with-trait-default-impl.rs:1:12
|
LL | #![feature(track_caller)]
| ^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

error[E0738]: `#[track_caller]` may not be used on trait methods
--> $DIR/error-with-trait-default-impl.rs:4:5
|
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// check-fail

#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(track_caller)]

trait Trait {
fn unwrap(&self);
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
warning: the feature `track_caller` is incomplete and may cause the compiler to crash
--> $DIR/error-with-trait-fn-impl.rs:3:12
|
LL | #![feature(track_caller)]
| ^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

error[E0738]: `#[track_caller]` may not be used on trait methods
--> $DIR/error-with-trait-fn-impl.rs:10:5
|
21 changes: 21 additions & 0 deletions src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// run-pass

#![feature(track_caller)]

macro_rules! caller_location_from_macro {
() => (core::panic::Location::caller());
}

fn main() {
let loc = core::panic::Location::caller();
assert_eq!(loc.file(), file!());
assert_eq!(loc.line(), 10);
assert_eq!(loc.column(), 15);

// `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
// i.e. point to where the macro was invoked, instead of the macro itself.
let loc2 = caller_location_from_macro!();
assert_eq!(loc2.file(), file!());
assert_eq!(loc2.line(), 17);
assert_eq!(loc2.column(), 16);
}
2 changes: 1 addition & 1 deletion src/test/ui/rfc-2091-track-caller/only-for-fns.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(track_caller)]

#[track_caller]
struct S;
8 changes: 0 additions & 8 deletions src/test/ui/rfc-2091-track-caller/only-for-fns.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
warning: the feature `track_caller` is incomplete and may cause the compiler to crash
--> $DIR/only-for-fns.rs:1:12
|
LL | #![feature(track_caller)]
| ^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

error[E0739]: attribute should be applied to function
--> $DIR/only-for-fns.rs:3:1
|
2 changes: 1 addition & 1 deletion src/test/ui/rfc-2091-track-caller/pass.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// run-pass
#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(track_caller)]

#[track_caller]
fn f() {}
8 changes: 0 additions & 8 deletions src/test/ui/rfc-2091-track-caller/pass.stderr

This file was deleted.

40 changes: 40 additions & 0 deletions src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// run-pass

#![feature(const_fn, track_caller)]

use std::panic::Location;

#[track_caller]
fn tracked() -> &'static Location<'static> {
Location::caller()
}

fn nested_intrinsic() -> &'static Location<'static> {
Location::caller()
}

fn nested_tracked() -> &'static Location<'static> {
tracked()
}

fn main() {
let location = Location::caller();
assert_eq!(location.file(), file!());
assert_eq!(location.line(), 21);
assert_eq!(location.column(), 20);

let tracked = tracked();
assert_eq!(tracked.file(), file!());
assert_eq!(tracked.line(), 26);
assert_eq!(tracked.column(), 19);

let nested = nested_intrinsic();
assert_eq!(nested.file(), file!());
assert_eq!(nested.line(), 13);
assert_eq!(nested.column(), 5);

let contained = nested_tracked();
assert_eq!(contained.file(), file!());
assert_eq!(contained.line(), 17);
assert_eq!(contained.column(), 5);
}