From 743964ad3fe566ca2ce5c2de14f8733887d283fd Mon Sep 17 00:00:00 2001 From: Adam Perry Date: Wed, 9 Oct 2019 08:25:41 -0700 Subject: [PATCH] Implement core::intrinsics::caller_location. Returns a `&core::panic::Location` corresponding to where it was called, also making `Location` a lang item. --- src/libcore/intrinsics.rs | 4 ++++ src/libcore/panic.rs | 1 + src/librustc/middle/lang_items.rs | 1 + src/librustc_codegen_llvm/builder.rs | 15 ++++++++++++ src/librustc_codegen_llvm/common.rs | 7 ++++++ src/librustc_codegen_ssa/mir/block.rs | 16 +++++++++++++ src/librustc_codegen_ssa/traits/consts.rs | 2 ++ src/librustc_codegen_ssa/traits/statics.rs | 2 ++ src/librustc_mir/const_eval.rs | 23 +++++++++++++++++++ src/librustc_typeck/check/intrinsic.rs | 18 ++++++++++++--- .../caller-location-intrinsic.rs | 9 ++++++++ 11 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 4655d39fb8f1..4e0f18b88fe0 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -696,6 +696,10 @@ extern "rust-intrinsic" { /// This will statically either panic, or do nothing. pub fn panic_if_uninhabited(); + /// Gets a reference to a static `Location` indicating where it was called. + #[cfg(not(bootstrap))] + pub fn caller_location() -> &'static crate::panic::Location<'static>; + /// Creates a value initialized to zero. /// /// `init` is unsafe because it returns a zeroed-out datum, diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs index 989fc96732a5..9428ff3ef15c 100644 --- a/src/libcore/panic.rs +++ b/src/libcore/panic.rs @@ -162,6 +162,7 @@ impl fmt::Display for PanicInfo<'_> { /// /// panic!("Normal panic"); /// ``` +#[cfg_attr(not(bootstrap), lang = "panic_location")] #[derive(Debug)] #[stable(feature = "panic_hooks", since = "1.10.0")] pub struct Location<'a> { diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index cab929389d6a..a17b5a115b86 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -366,6 +366,7 @@ language_item_table! { PanicFnLangItem, "panic", panic_fn, Target::Fn; PanicBoundsCheckFnLangItem, "panic_bounds_check", panic_bounds_check_fn, Target::Fn; PanicInfoLangItem, "panic_info", panic_info, Target::Struct; + PanicLocationLangItem, "panic_location", panic_location, Target::Struct; PanicImplLangItem, "panic_impl", panic_impl, Target::Fn; // Libstd panic entry point. Necessary for const eval to be able to catch it BeginPanicFnLangItem, "begin_panic", begin_panic_fn, Target::Fn; diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index 98be0ae44335..ffaf8050bcbb 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -2,6 +2,7 @@ use crate::llvm::{AtomicRmwBinOp, AtomicOrdering, SynchronizationScope}; use crate::llvm::{self, False, BasicBlock}; use crate::common::Funclet; use crate::context::CodegenCx; +use crate::syntax_pos::Pos; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; @@ -1068,6 +1069,20 @@ impl StaticBuilderMethods for Builder<'a, 'll, 'tcx> { self.cx().get_static(def_id) } + fn static_panic_location(&mut self, loc: &syntax::source_map::Loc) -> Self::Value { + let filename = Symbol::intern(&loc.file.name.to_string()); + let filename = self.const_str(filename); + let line = self.const_u32(loc.line as u32); + let col = self.const_u32(loc.col.to_usize() as u32 + 1); + let struct_ = self.const_struct(&[filename.0, filename.1, line, col], false); + + let align = self.tcx.data_layout.aggregate_align.abi + .max(self.tcx.data_layout.i32_align.abi) + .max(self.tcx.data_layout.pointer_align.abi); + // FIXME(eddyb) move this into miri, it can be correct if e.g. field order changes + self.static_addr_of(struct_, align, Some("panic_loc")) + } + fn static_panic_msg( &mut self, msg: Option, diff --git a/src/librustc_codegen_llvm/common.rs b/src/librustc_codegen_llvm/common.rs index a1a5232d5883..b4b82f67c74b 100644 --- a/src/librustc_codegen_llvm/common.rs +++ b/src/librustc_codegen_llvm/common.rs @@ -237,6 +237,13 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMConstReal(t, val) } } + fn const_str(&self, s: Symbol) -> (&'ll Value, &'ll Value) { + let len = s.as_str().len(); + let cs = consts::ptrcast(self.const_cstr(s, false), + self.type_ptr_to(self.layout_of(self.tcx.mk_str()).llvm_type(self))); + (cs, self.const_usize(len as u64)) + } + fn const_struct( &self, elts: &[&'ll Value], diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 28441cae26e3..22ea6a395521 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -613,6 +613,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ReturnDest::Nothing }; + if intrinsic == Some("caller_location") { + if let Some((_, target)) = destination.as_ref() { + let loc = bx.sess().source_map().lookup_char_pos(span.lo()); + let location = bx.static_panic_location(&loc); + + if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { + Immediate(location).store(&mut bx, tmp); + } + self.store_return(&mut bx, ret_dest, &fn_ty.ret, location); + + helper.maybe_sideeffect(self.mir, &mut bx, &[*target]); + helper.funclet_br(self, &mut bx, *target); + } + return; + } + if intrinsic.is_some() && intrinsic != Some("drop_in_place") { let dest = match ret_dest { _ if fn_ty.ret.is_indirect() => llargs[0], diff --git a/src/librustc_codegen_ssa/traits/consts.rs b/src/librustc_codegen_ssa/traits/consts.rs index 95ada60fae08..8c462e77d5e0 100644 --- a/src/librustc_codegen_ssa/traits/consts.rs +++ b/src/librustc_codegen_ssa/traits/consts.rs @@ -3,6 +3,7 @@ use crate::mir::place::PlaceRef; use rustc::mir::interpret::Allocation; use rustc::mir::interpret::Scalar; use rustc::ty::layout; +use syntax_pos::Symbol; pub trait ConstMethods<'tcx>: BackendTypes { // Constant constructors @@ -19,6 +20,7 @@ pub trait ConstMethods<'tcx>: BackendTypes { fn const_u8(&self, i: u8) -> Self::Value; fn const_real(&self, t: Self::Type, val: f64) -> Self::Value; + fn const_str(&self, s: Symbol) -> (Self::Value, Self::Value); fn const_struct(&self, elts: &[Self::Value], packed: bool) -> Self::Value; fn const_to_opt_uint(&self, v: Self::Value) -> Option; diff --git a/src/librustc_codegen_ssa/traits/statics.rs b/src/librustc_codegen_ssa/traits/statics.rs index 73c4c0539791..b51f15b5823e 100644 --- a/src/librustc_codegen_ssa/traits/statics.rs +++ b/src/librustc_codegen_ssa/traits/statics.rs @@ -1,4 +1,5 @@ use super::BackendTypes; +use syntax::source_map::Loc; use syntax_pos::symbol::Symbol; use rustc::hir::def_id::DefId; use rustc::ty::layout::Align; @@ -10,6 +11,7 @@ pub trait StaticMethods: BackendTypes { pub trait StaticBuilderMethods: BackendTypes { fn get_static(&mut self, def_id: DefId) -> Self::Value; + fn static_panic_location(&mut self, loc: &Loc) -> Self::Value; fn static_panic_msg( &mut self, msg: Option, diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index bb02b99dd8d8..eeaa3c6792d3 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -9,6 +9,7 @@ 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}; @@ -505,6 +506,28 @@ pub fn const_field<'tcx>( op_to_const(&ecx, field) } +pub fn const_caller_location<'tcx>( + tcx: TyCtxt<'tcx>, + (file, line, col): (Symbol, u32, u32), +) -> &'tcx ty::Const<'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_place = ecx.alloc_caller_location(file, line, col).unwrap(); + intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap(); + let loc_const = ty::Const { + ty: loc_ty, + val: ConstValue::Scalar(loc_place.ptr.into()), + }; + + tcx.mk_const(loc_const) +} + // this function uses `unwrap` copiously, because an already validated constant must have valid // fields and can thus never fail outside of compiler bugs pub fn const_variant_index<'tcx>( diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 72a0fe887b96..2c5757b1c9fd 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -1,6 +1,8 @@ //! Type-checking for the rust-intrinsic and platform-intrinsic //! intrinsics that the compiler exposes. +use rustc::hir::{self, Mutability}; +use rustc::middle::lang_items::PanicLocationLangItem; use rustc::traits::{ObligationCause, ObligationCauseCode}; use rustc::ty::{self, TyCtxt, Ty}; use rustc::ty::subst::Subst; @@ -9,8 +11,6 @@ use crate::require_same_types; use rustc_target::spec::abi::Abi; use syntax::symbol::Symbol; -use rustc::hir; - use std::iter; fn equate_intrinsic_type<'tcx>( @@ -65,7 +65,7 @@ fn equate_intrinsic_type<'tcx>( /// Returns `true` if the given intrinsic is unsafe to call or not. pub fn intrinsic_operation_unsafety(intrinsic: &str) -> hir::Unsafety { match intrinsic { - "size_of" | "min_align_of" | "needs_drop" | + "size_of" | "min_align_of" | "needs_drop" | "caller_location" | "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" | "wrapping_add" | "wrapping_sub" | "wrapping_mul" | "saturating_add" | "saturating_sub" | @@ -143,6 +143,18 @@ 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_ref( + tcx.lifetimes.re_static, + ty::TypeAndMut { + mutbl: Mutability::MutImmutable, + ty: tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None)) + .subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())), + }, + ), + ), "panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()), "init" => (1, Vec::new(), param(0)), "uninit" => (1, Vec::new(), param(0)), diff --git a/src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs b/src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs new file mode 100644 index 000000000000..ab6c59384c43 --- /dev/null +++ b/src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs @@ -0,0 +1,9 @@ +// run-pass + +#![feature(core_intrinsics)] +fn main() { + let loc = core::intrinsics::caller_location(); + assert_eq!(loc.file(), file!()); + assert_eq!(loc.line(), 5); + assert_eq!(loc.column(), 15); +}