提交 de17464b 编写于 作者: B bors

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](https://github.com/rust-lang/rust/pull/65664)
[RFC 2091 text](https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md)
[Tracking issue](https://github.com/rust-lang/rust/issues/47809)
[Tracking doc](https://paper.dropbox.com/doc/track_rfc_2091_impl-notes--Anf1NwnIb0xcRv31YLIadyj0Ag-rwCdRc2fi2yvRZ7syGZ9q#:uid=863513134494965680023183&h2=TODO-actually-pass-location-to)
......@@ -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)]
......
......@@ -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!` \
......
......@@ -8,6 +8,7 @@
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 @@ pub fn is_bound_region_in_impl_item(
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> {
......
......@@ -116,6 +116,10 @@ pub fn requires_local(&self, tcx: TyCtxt<'tcx>) -> bool {
}
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 @@ pub fn resolve_for_fn_ptr(
) -> 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);
}
......
......@@ -2434,6 +2434,7 @@ fn new_internal(
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 @@ impl<'tcx, C> FnAbiExt<'tcx, C> for call::FnAbi<'tcx, Ty<'tcx>>
+ 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 @@ fn new_internal(
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 @@ fn new_internal(
.iter()
.cloned()
.chain(extra_args)
.chain(caller_location)
.enumerate()
.map(|(i, ty)| arg_of(ty, Some(i)))
.collect(),
......
......@@ -774,6 +774,18 @@ fn codegen_call_terminator(
&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),
......@@ -1010,14 +1022,16 @@ fn get_caller_location(
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(
......
......@@ -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;
......
......@@ -541,5 +541,4 @@ pub fn set(&self, features: &mut Features, span: Span) {
sym::or_patterns,
sym::let_chains,
sym::raw_dylib,
sym::track_caller,
];
......@@ -9,11 +9,10 @@
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 @@ fn find_mir_or_eval_fn(
//
// 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 {
......
......@@ -112,6 +112,7 @@ pub fn emulate_intrinsic(
// `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)?;
}
......
......@@ -6,6 +6,21 @@
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,
......
......@@ -230,6 +230,11 @@ fn should_inline(&self,
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
......
//! 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)),
......
......@@ -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;
......
// run-pass
#![feature(const_fn, core_intrinsics)]
use std::{intrinsics::caller_location, panic::Location};
const LOCATION: &Location = caller_location();
const NESTED: &Location = {
const fn nested_location() -> &'static Location<'static> {
caller_location()
};
nested_location()
};
fn main() {
assert_eq!(LOCATION.file(), file!());
assert_eq!(LOCATION.line(), 7);
assert_eq!(LOCATION.column(), 29);
assert_eq!(NESTED.file(), file!());
assert_eq!(NESTED.line(), 10);
assert_eq!(NESTED.column(), 9);
}
// 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);
}
// 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);
}
#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(track_caller)]
#[track_caller(1)]
fn f() {}
......
......@@ -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
#![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() {}
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]
......
#![feature(naked_functions, track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(naked_functions, track_caller)]
#[track_caller]
#[naked]
......
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
|
......
#![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
......
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
|
......
#![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
......
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
|
......
// check-fail
#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(track_caller)]
trait Trait {
fn unwrap(&self);
......
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
|
......
// 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);
}
#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(track_caller)]
#[track_caller]
struct S;
......
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
|
......
// run-pass
#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
#![feature(track_caller)]
#[track_caller]
fn f() {}
......
warning: the feature `track_caller` is incomplete and may cause the compiler to crash
--> $DIR/pass.rs:2:12
|
LL | #![feature(track_caller)]
| ^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
// 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);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册