Implement core::intrinsics::caller_location.

Returns a `&core::panic::Location` corresponding to where it was
called, also making `Location` a lang item.
This commit is contained in:
Adam Perry 2019-10-09 08:25:41 -07:00
parent fcf516d827
commit 743964ad3f
11 changed files with 95 additions and 3 deletions

View File

@ -696,6 +696,10 @@ extern "rust-intrinsic" {
/// This will statically either panic, or do nothing.
pub fn panic_if_uninhabited<T>();
/// 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,

View File

@ -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> {

View File

@ -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;

View File

@ -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<Symbol>,

View File

@ -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],

View File

@ -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],

View File

@ -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<u64>;

View File

@ -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<Symbol>,

View File

@ -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>(

View File

@ -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)),

View File

@ -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);
}