diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 4655d39fb8f..4e0f18b88fe 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/macros.rs b/src/libcore/macros.rs index 35558e3abcd..8ccd31c95d5 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -1,8 +1,9 @@ /// Panics the current thread. /// /// For details, see `std::macros`. +#[cfg(bootstrap)] #[macro_export] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(core_panic, panic_internals)] #[stable(feature = "core", since = "1.6.0")] macro_rules! panic { () => ( @@ -20,6 +21,38 @@ macro_rules! panic { }); } +/// Panics the current thread. +/// +/// For details, see `std::macros`. +#[cfg(not(bootstrap))] +#[macro_export] +#[allow_internal_unstable(core_panic, panic_internals)] +#[stable(feature = "core", since = "1.6.0")] +macro_rules! panic { + () => ( + $crate::panic!("explicit panic") + ); + ($msg:expr) => ({ + const LOC: &$crate::panic::Location<'_> = &$crate::panic::Location::internal_constructor( + $crate::file!(), + $crate::line!(), + $crate::column!(), + ); + $crate::panicking::panic($msg, LOC) + }); + ($msg:expr,) => ( + $crate::panic!($msg) + ); + ($fmt:expr, $($arg:tt)+) => ({ + const LOC: &$crate::panic::Location<'_> = &$crate::panic::Location::internal_constructor( + $crate::file!(), + $crate::line!(), + $crate::column!(), + ); + $crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+), LOC) + }); +} + /// Asserts that two expressions are equal to each other (using [`PartialEq`]). /// /// On panic, this macro will print the values of the expressions with their diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs index 989fc96732a..51bbf3a8fd2 100644 --- a/src/libcore/panic.rs +++ b/src/libcore/panic.rs @@ -35,7 +35,7 @@ use crate::fmt; pub struct PanicInfo<'a> { payload: &'a (dyn Any + Send), message: Option<&'a fmt::Arguments<'a>>, - location: Location<'a>, + location: &'a Location<'a>, } impl<'a> PanicInfo<'a> { @@ -45,11 +45,16 @@ impl<'a> PanicInfo<'a> { issue = "0")] #[doc(hidden)] #[inline] - pub fn internal_constructor(message: Option<&'a fmt::Arguments<'a>>, - location: Location<'a>) - -> Self { + pub fn internal_constructor( + message: Option<&'a fmt::Arguments<'a>>, + location: &'a Location<'a>, + ) -> Self { struct NoPayload; - PanicInfo { payload: &NoPayload, location, message } + PanicInfo { + location, + message, + payload: &NoPayload, + } } #[doc(hidden)] @@ -162,6 +167,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> { @@ -176,7 +182,7 @@ impl<'a> Location<'a> { and related macros", issue = "0")] #[doc(hidden)] - pub fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self { + pub const fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self { Location { file, line, col } } diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index e8f0561604a..685b749776b 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -29,6 +29,7 @@ use crate::fmt; use crate::panic::{Location, PanicInfo}; +#[cfg(bootstrap)] #[cold] // never inline unless panic_immediate_abort to avoid code // bloat at the call sites as much as possible @@ -49,6 +50,27 @@ pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! { panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), &(file, line, col)) } +#[cfg(not(bootstrap))] +#[cold] +// never inline unless panic_immediate_abort to avoid code +// bloat at the call sites as much as possible +#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] +#[lang = "panic"] +pub fn panic(expr: &str, location: &Location<'_>) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { super::intrinsics::abort() } + } + + // Use Arguments::new_v1 instead of format_args!("{}", expr) to potentially + // reduce size overhead. The format_args! macro uses str's Display trait to + // write expr, which calls Formatter::pad, which must accommodate string + // truncation and padding (even though none is used here). Using + // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the + // output binary, saving up to a few kilobytes. + panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), location) +} + +#[cfg(bootstrap)] #[cold] #[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] #[lang = "panic_bounds_check"] @@ -62,6 +84,22 @@ fn panic_bounds_check(file_line_col: &(&'static str, u32, u32), len, index), file_line_col) } +#[cfg(not(bootstrap))] +#[cold] +#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] +#[lang = "panic_bounds_check"] +fn panic_bounds_check(location: &Location<'_>, index: usize, len: usize) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { super::intrinsics::abort() } + } + + panic_fmt( + format_args!("index out of bounds: the len is {} but the index is {}", len, index), + location + ) +} + +#[cfg(bootstrap)] #[cold] #[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] #[cfg_attr( feature="panic_immediate_abort" ,inline)] @@ -77,9 +115,26 @@ pub fn panic_fmt(fmt: fmt::Arguments<'_>, file_line_col: &(&'static str, u32, u3 } let (file, line, col) = *file_line_col; - let pi = PanicInfo::internal_constructor( - Some(&fmt), - Location::internal_constructor(file, line, col), - ); + let location = Location::internal_constructor(file, line, col); + let pi = PanicInfo::internal_constructor(Some(&fmt), &location); + unsafe { panic_impl(&pi) } +} + +#[cfg(not(bootstrap))] +#[cold] +#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] +#[cfg_attr( feature="panic_immediate_abort" ,inline)] +pub fn panic_fmt(fmt: fmt::Arguments<'_>, location: &Location<'_>) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { super::intrinsics::abort() } + } + + // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call + extern "Rust" { + #[lang = "panic_impl"] + fn panic_impl(pi: &PanicInfo<'_>) -> !; + } + + let pi = PanicInfo::internal_constructor(Some(&fmt), location); unsafe { panic_impl(&pi) } } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 0db79785282..72fb1fd3561 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -370,6 +370,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/query/mod.rs b/src/librustc/query/mod.rs index 7cae74b59c7..f628e194748 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -466,6 +466,12 @@ rustc_queries! { no_force desc { "extract field of const" } } + + query const_caller_location(key: (syntax_pos::Symbol, u32, u32)) -> &'tcx ty::Const<'tcx> { + eval_always + no_force + desc { "get a &core::panic::Location referring to a span" } + } } TypeChecking { diff --git a/src/librustc/ty/query/keys.rs b/src/librustc/ty/query/keys.rs index 0a217e9ae66..a9e0a5d6ab5 100644 --- a/src/librustc/ty/query/keys.rs +++ b/src/librustc/ty/query/keys.rs @@ -208,3 +208,13 @@ impl<'tcx, T> Key for Canonical<'tcx, T> { DUMMY_SP } } + +impl Key for (Symbol, u32, u32) { + fn query_crate(&self) -> CrateNum { + LOCAL_CRATE + } + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index 98be0ae4433..312c41b88b0 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -23,7 +23,6 @@ use std::ffi::CStr; use std::ops::{Deref, Range}; use std::ptr; use std::iter::TrustedLen; -use syntax::symbol::Symbol; // All Builders must have an llfn associated with them #[must_use] @@ -1067,36 +1066,6 @@ impl StaticBuilderMethods for Builder<'a, 'll, 'tcx> { // Forward to the `get_static` method of `CodegenCx` self.cx().get_static(def_id) } - - fn static_panic_msg( - &mut self, - msg: Option, - filename: Symbol, - line: Self::Value, - col: Self::Value, - kind: &str, - ) -> Self::Value { - 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); - - let filename = self.const_str_slice(filename); - - let with_msg_components; - let without_msg_components; - - let components = if let Some(msg) = msg { - let msg = self.const_str_slice(msg); - with_msg_components = [msg, filename, line, col]; - &with_msg_components as &[_] - } else { - without_msg_components = [filename, line, col]; - &without_msg_components as &[_] - }; - - let struct_ = self.const_struct(&components, false); - self.static_addr_of(struct_, align, Some(kind)) - } } impl Builder<'a, 'll, 'tcx> { diff --git a/src/librustc_codegen_llvm/common.rs b/src/librustc_codegen_llvm/common.rs index a1a5232d588..f38f9dfecd3 100644 --- a/src/librustc_codegen_llvm/common.rs +++ b/src/librustc_codegen_llvm/common.rs @@ -3,7 +3,6 @@ //! Code that is useful in various codegen modules. use crate::llvm::{self, True, False, Bool, BasicBlock, OperandBundleDef, ConstantInt}; -use crate::abi; use crate::consts; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; @@ -96,16 +95,6 @@ impl BackendTypes for CodegenCx<'ll, 'tcx> { } impl CodegenCx<'ll, 'tcx> { - pub fn const_fat_ptr( - &self, - ptr: &'ll Value, - meta: &'ll Value - ) -> &'ll Value { - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); - self.const_struct(&[ptr, meta], false) - } - pub fn const_array(&self, ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value { unsafe { return llvm::LLVMConstArray(ty, elts.as_ptr(), elts.len() as c_uint); @@ -150,13 +139,6 @@ impl CodegenCx<'ll, 'tcx> { } } - pub fn const_str_slice(&self, s: Symbol) -> &'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))); - self.const_fat_ptr(cs, self.const_usize(len as u64)) - } - pub fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value { unsafe { assert_eq!(idx as c_uint as u64, idx); @@ -237,6 +219,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 28441cae26e..79855311f37 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -15,8 +15,7 @@ use crate::traits::*; use std::borrow::Cow; -use syntax::symbol::Symbol; -use syntax_pos::Pos; +use syntax::{source_map::Span, symbol::Symbol}; use super::{FunctionCx, LocalRef}; use super::place::PlaceRef; @@ -421,38 +420,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(&mut bx, terminator.source_info); // Get the location information. - let loc = bx.sess().source_map().lookup_char_pos(span.lo()); - let filename = Symbol::intern(&loc.file.name.to_string()); - let line = bx.const_u32(loc.line as u32); - let col = bx.const_u32(loc.col.to_usize() as u32 + 1); + let location = self.get_caller_location(&mut bx, span).immediate(); // Put together the arguments to the panic entry point. let (lang_item, args) = match msg { PanicInfo::BoundsCheck { ref len, ref index } => { let len = self.codegen_operand(&mut bx, len).immediate(); let index = self.codegen_operand(&mut bx, index).immediate(); - - let file_line_col = bx.static_panic_msg( - None, - filename, - line, - col, - "panic_bounds_check_loc", - ); - (lang_items::PanicBoundsCheckFnLangItem, - vec![file_line_col, index, len]) + (lang_items::PanicBoundsCheckFnLangItem, vec![location, index, len]) } _ => { let msg_str = Symbol::intern(msg.description()); - let msg_file_line_col = bx.static_panic_msg( - Some(msg_str), - filename, - line, - col, - "panic_loc", - ); - (lang_items::PanicFnLangItem, - vec![msg_file_line_col]) + let msg = bx.const_str(msg_str); + (lang_items::PanicFnLangItem, vec![msg.0, msg.1, location]) } }; @@ -553,23 +533,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = instance.unwrap().substs.type_at(0); let layout = bx.layout_of(ty); if layout.abi.is_uninhabited() { - let loc = bx.sess().source_map().lookup_char_pos(span.lo()); - let filename = Symbol::intern(&loc.file.name.to_string()); - let line = bx.const_u32(loc.line as u32); - let col = bx.const_u32(loc.col.to_usize() as u32 + 1); - - let str = format!( - "Attempted to instantiate uninhabited type {}", - ty - ); - let msg_str = Symbol::intern(&str); - let msg_file_line_col = bx.static_panic_msg( - Some(msg_str), - filename, - line, - col, - "panic_loc", - ); + let msg_str = format!("Attempted to instantiate uninhabited type {}", ty); + let msg = bx.const_str(Symbol::intern(&msg_str)); + let location = self.get_caller_location(&mut bx, span).immediate(); // Obtain the panic entry point. let def_id = @@ -587,7 +553,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &mut bx, fn_ty, llfn, - &[msg_file_line_col], + &[msg.0, msg.1, location], destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)), cleanup, ); @@ -613,6 +579,21 @@ 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 location = self.get_caller_location(&mut bx, span); + + if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { + location.val.store(&mut bx, tmp); + } + self.store_return(&mut bx, ret_dest, &fn_ty.ret, location.immediate()); + + 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], @@ -1009,6 +990,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + fn get_caller_location( + &mut self, + bx: &mut Bx, + span: Span, + ) -> OperandRef<'tcx, Bx::Value> { + let caller = bx.tcx().sess.source_map().lookup_char_pos(span.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( &mut self, bx: &mut Bx diff --git a/src/librustc_codegen_ssa/traits/consts.rs b/src/librustc_codegen_ssa/traits/consts.rs index 95ada60fae0..8c462e77d5e 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 73c4c053979..5c108f9fa6c 100644 --- a/src/librustc_codegen_ssa/traits/statics.rs +++ b/src/librustc_codegen_ssa/traits/statics.rs @@ -1,5 +1,4 @@ use super::BackendTypes; -use syntax_pos::symbol::Symbol; use rustc::hir::def_id::DefId; use rustc::ty::layout::Align; @@ -10,12 +9,4 @@ pub trait StaticMethods: BackendTypes { pub trait StaticBuilderMethods: BackendTypes { fn get_static(&mut self, def_id: DefId) -> Self::Value; - fn static_panic_msg( - &mut self, - msg: Option, - filename: Symbol, - line: Self::Value, - col: Self::Value, - kind: &str, - ) -> Self::Value; } diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index bb02b99dd8d..89bdf7391c3 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}; @@ -17,7 +18,7 @@ use rustc::traits::Reveal; use rustc_data_structures::fx::FxHashMap; use crate::interpret::eval_nullary_intrinsic; -use syntax::source_map::{Span, DUMMY_SP}; +use syntax::{source_map::{Span, DUMMY_SP}, symbol::Symbol}; use crate::interpret::{self, PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer, @@ -158,11 +159,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( ecx.run()?; // Intern the result - intern_const_alloc_recursive( - ecx, - cid.instance.def_id(), - ret, - )?; + intern_const_alloc_recursive(ecx, tcx.static_mutability(cid.instance.def_id()), ret)?; debug!("eval_body_using_ecx done: {:?}", *ret); Ok(ret) @@ -374,11 +371,12 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, fn call_intrinsic( ecx: &mut InterpCx<'mir, 'tcx, Self>, + span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], dest: PlaceTy<'tcx>, ) -> InterpResult<'tcx> { - if ecx.emulate_intrinsic(instance, args, dest)? { + if ecx.emulate_intrinsic(span, instance, args, dest)? { return Ok(()); } // An intrinsic that we do not support @@ -505,6 +503,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_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs index 646d1783c8e..924529d7f55 100644 --- a/src/librustc_mir/interpret/intern.rs +++ b/src/librustc_mir/interpret/intern.rs @@ -6,7 +6,6 @@ use rustc::ty::{Ty, self}; use rustc::mir::interpret::{InterpResult, ErrorHandled}; use rustc::hir; -use rustc::hir::def_id::DefId; use super::validity::RefTracking; use rustc_data_structures::fx::FxHashSet; @@ -270,12 +269,12 @@ for pub fn intern_const_alloc_recursive( ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, - def_id: DefId, + // The `mutability` of the place, ignoring the type. + place_mut: Option, ret: MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let tcx = ecx.tcx; - // this `mutability` is the mutability of the place, ignoring the type - let (base_mutability, base_intern_mode) = match tcx.static_mutability(def_id) { + let (base_mutability, base_intern_mode) = match place_mut { Some(hir::Mutability::MutImmutable) => (Mutability::Immutable, InternMode::Static), // `static mut` doesn't care about interior mutability, it's mutable anyway Some(hir::Mutability::MutMutable) => (Mutability::Mutable, InternMode::Static), diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 5fc23b4a69e..519f4f03222 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -3,6 +3,7 @@ //! and miri. use syntax::symbol::Symbol; +use syntax_pos::Span; use rustc::ty; use rustc::ty::layout::{LayoutOf, Primitive, Size}; use rustc::ty::subst::SubstsRef; @@ -15,6 +16,7 @@ use super::{ Machine, PlaceTy, OpTy, InterpCx, }; +mod caller_location; mod type_name; fn numeric_intrinsic<'tcx, Tag>( @@ -86,6 +88,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Returns `true` if emulation happened. pub fn emulate_intrinsic( &mut self, + span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, M::PointerTag>], dest: PlaceTy<'tcx, M::PointerTag>, @@ -94,6 +97,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..]; match intrinsic_name { + "caller_location" => { + let caller = self.tcx.sess.source_map().lookup_char_pos(span.lo()); + let location = self.alloc_caller_location( + Symbol::intern(&caller.file.name.to_string()), + caller.line as u32, + caller.col_display as u32 + 1, + )?; + self.write_scalar(location.ptr, dest)?; + } + "min_align_of" | "pref_align_of" | "needs_drop" | @@ -301,18 +314,19 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx, bool> { let def_id = instance.def_id(); if Some(def_id) == self.tcx.lang_items().panic_fn() { - assert!(args.len() == 1); - // &(&'static str, &'static str, u32, u32) - let place = self.deref_operand(args[0])?; - let (msg, file, line, col) = ( - self.mplace_field(place, 0)?, - self.mplace_field(place, 1)?, - self.mplace_field(place, 2)?, - self.mplace_field(place, 3)?, + // &'static str, &core::panic::Location { &'static str, u32, u32 } + assert!(args.len() == 2); + + let msg_place = self.deref_operand(args[0])?; + let msg = Symbol::intern(self.read_str(msg_place)?); + + let location = self.deref_operand(args[1])?; + let (file, line, col) = ( + self.mplace_field(location, 0)?, + self.mplace_field(location, 1)?, + self.mplace_field(location, 2)?, ); - let msg_place = self.deref_operand(msg.into())?; - let msg = Symbol::intern(self.read_str(msg_place)?); let file_place = self.deref_operand(file.into())?; let file = Symbol::intern(self.read_str(file_place)?); let line = self.read_scalar(line.into())?.to_u32()?; diff --git a/src/librustc_mir/interpret/intrinsics/caller_location.rs b/src/librustc_mir/interpret/intrinsics/caller_location.rs new file mode 100644 index 00000000000..249d2f9ff53 --- /dev/null +++ b/src/librustc_mir/interpret/intrinsics/caller_location.rs @@ -0,0 +1,49 @@ +use rustc::middle::lang_items::PanicLocationLangItem; +use rustc::mir::interpret::{Pointer, PointerArithmetic, Scalar}; +use rustc::ty::subst::Subst; +use rustc_target::abi::{LayoutOf, Size}; +use syntax_pos::Symbol; + +use crate::interpret::{MemoryKind, MPlaceTy, intrinsics::{InterpCx, InterpResult, Machine}}; + +impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { + pub fn alloc_caller_location( + &mut self, + filename: Symbol, + line: u32, + col: u32, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + let line = Scalar::from_u32(line); + let col = Scalar::from_u32(col); + + let ptr_size = self.pointer_size(); + let u32_size = Size::from_bits(32); + + let loc_ty = self.tcx.type_of(self.tcx.require_lang_item(PanicLocationLangItem, None)) + .subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_static.into()].iter())); + let loc_layout = self.layout_of(loc_ty)?; + + let file_alloc = self.tcx.allocate_bytes(filename.as_str().as_bytes()); + let file_ptr = Pointer::new(file_alloc, Size::ZERO); + let file = Scalar::Ptr(self.tag_static_base_pointer(file_ptr)); + let file_len = Scalar::from_uint(filename.as_str().len() as u128, ptr_size); + + let location = self.allocate(loc_layout, MemoryKind::Stack); + + let file_out = self.mplace_field(location, 0)?; + let file_ptr_out = self.force_ptr(self.mplace_field(file_out, 0)?.ptr)?; + let file_len_out = self.force_ptr(self.mplace_field(file_out, 1)?.ptr)?; + let line_out = self.force_ptr(self.mplace_field(location, 1)?.ptr)?; + let col_out = self.force_ptr(self.mplace_field(location, 2)?.ptr)?; + + let layout = &self.tcx.data_layout; + let alloc = self.memory.get_mut(file_ptr_out.alloc_id)?; + + alloc.write_scalar(layout, file_ptr_out, file.into(), ptr_size)?; + alloc.write_scalar(layout, file_len_out, file_len.into(), ptr_size)?; + alloc.write_scalar(layout, line_out, line.into(), u32_size)?; + alloc.write_scalar(layout, col_out, col.into(), u32_size)?; + + Ok(location) + } +} diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index c30c59bbf10..870e50a3cbb 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -8,6 +8,7 @@ use std::hash::Hash; use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::{self, Ty, TyCtxt}; +use syntax_pos::Span; use super::{ Allocation, AllocId, InterpResult, Scalar, AllocationExtra, @@ -152,6 +153,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// If this returns successfully, the engine will take care of jumping to the next block. fn call_intrinsic( ecx: &mut InterpCx<'mir, 'tcx, Self>, + span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, Self::PointerTag>], dest: PlaceTy<'tcx, Self::PointerTag>, diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 7f6baf0bb49..d90f2058aa7 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -255,7 +255,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Some(dest) => dest, None => throw_ub!(Unreachable) }; - M::call_intrinsic(self, instance, args, dest)?; + M::call_intrinsic(self, span, instance, args, dest)?; // No stack frame gets pushed, the main loop will just act as if the // call completed. self.goto_block(ret)?; diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 98d5487870a..4d604cb025c 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -58,6 +58,7 @@ pub fn provide(providers: &mut Providers<'_>) { providers.const_eval = const_eval::const_eval_provider; providers.const_eval_raw = const_eval::const_eval_raw_provider; providers.check_match = hair::pattern::check_match; + providers.const_caller_location = const_eval::const_caller_location; providers.const_field = |tcx, param_env_and_value| { let (param_env, (value, field)) = param_env_and_value.into_parts(); const_eval::const_field(tcx, param_env, None, field, value) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 13097a21561..e7095101f46 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -158,6 +158,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { fn call_intrinsic( _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _span: Span, _instance: ty::Instance<'tcx>, _args: &[OpTy<'tcx>], _dest: PlaceTy<'tcx>, diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 72a0fe887b9..76cc7062d3b 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -1,6 +1,7 @@ //! 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; @@ -65,7 +66,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 +144,15 @@ 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())), + ), + ), "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/libstd/panicking.rs b/src/libstd/panicking.rs index 619b1820190..f76969146fd 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -323,10 +323,8 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments<'_>, } let (file, line, col) = *file_line_col; - let info = PanicInfo::internal_constructor( - Some(msg), - Location::internal_constructor(file, line, col), - ); + let location = Location::internal_constructor(file, line, col); + let info = PanicInfo::internal_constructor(Some(msg), &location); continue_panic_fmt(&info) } @@ -453,10 +451,8 @@ fn rust_panic_with_hook(payload: &mut dyn BoxMeUp, } unsafe { - let mut info = PanicInfo::internal_constructor( - message, - Location::internal_constructor(file, line, col), - ); + let location = Location::internal_constructor(file, line, col); + let mut info = PanicInfo::internal_constructor(message, &location); HOOK_LOCK.read(); match HOOK { // Some platforms know that printing to stderr won't ever actually diff --git a/src/test/ui/consts/const-eval/const_caller_location.rs b/src/test/ui/consts/const-eval/const_caller_location.rs new file mode 100644 index 00000000000..c63822f052b --- /dev/null +++ b/src/test/ui/consts/const-eval/const_caller_location.rs @@ -0,0 +1,23 @@ +// 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); +} diff --git a/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs b/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs index ff10d412a11..8727c9d1ca6 100644 --- a/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs +++ b/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs @@ -22,7 +22,7 @@ //[thin]compile-flags: -C lto=thin //[fat]compile-flags: -C lto=fat -#![feature(core_panic)] +#![feature(core_panic, panic_internals)] // (For some reason, reproducing the LTO issue requires pulling in std // explicitly this way.) @@ -51,7 +51,8 @@ fn main() { let _guard = Droppable; let s = "issue-64655-allow-unwind-when-calling-panic-directly.rs"; - core::panicking::panic(&("???", s, 17, 4)); + let location = core::panic::Location::internal_constructor(s, 17, 4); + core::panicking::panic("???", &location); }); let wait = handle.join(); 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 00000000000..ab6c59384c4 --- /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); +}