about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-10-28 04:53:06 +0100
committerGitHub <noreply@github.com>2019-10-28 04:53:06 +0100
commit4728d66206c82c98aa4e1fad751e8aae7489c242 (patch)
treea95e0d577f867d3fba9346478e13bcac3dc793a8 /src
parentc8eefdffe9480bbd84554eab3343f71f04af8f9a (diff)
parent86e55b1882ac492d6234e5c5f97c7f151a11f5d2 (diff)
downloadrust-4728d66206c82c98aa4e1fad751e8aae7489c242.tar.gz
rust-4728d66206c82c98aa4e1fad751e8aae7489c242.zip
Rollup merge of #65664 - anp:panic-location, r=eddyb
`std::panic::Location` is a lang_item, add `core::intrinsics::caller_location` (RFC 2091 3/N)

[Tracking issue](https://github.com/rust-lang/rust/issues/47809)
[RFC text](https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md)

@eddyb suggested doing this intrinsic implementation ahead of actually implementing the `#[track_caller]` attribute so that there's an easily tested intermediate step between adding the shim and wiring up the attribute.
Diffstat (limited to 'src')
-rw-r--r--src/libcore/intrinsics.rs4
-rw-r--r--src/libcore/macros.rs35
-rw-r--r--src/libcore/panic.rs18
-rw-r--r--src/libcore/panicking.rs63
-rw-r--r--src/librustc/middle/lang_items.rs1
-rw-r--r--src/librustc/query/mod.rs6
-rw-r--r--src/librustc/ty/query/keys.rs10
-rw-r--r--src/librustc_codegen_llvm/builder.rs31
-rw-r--r--src/librustc_codegen_llvm/common.rs25
-rw-r--r--src/librustc_codegen_ssa/mir/block.rs81
-rw-r--r--src/librustc_codegen_ssa/traits/consts.rs2
-rw-r--r--src/librustc_codegen_ssa/traits/statics.rs9
-rw-r--r--src/librustc_mir/const_eval.rs34
-rw-r--r--src/librustc_mir/interpret/intern.rs7
-rw-r--r--src/librustc_mir/interpret/intrinsics.rs34
-rw-r--r--src/librustc_mir/interpret/intrinsics/caller_location.rs49
-rw-r--r--src/librustc_mir/interpret/machine.rs2
-rw-r--r--src/librustc_mir/interpret/terminator.rs2
-rw-r--r--src/librustc_mir/lib.rs1
-rw-r--r--src/librustc_mir/transform/const_prop.rs1
-rw-r--r--src/librustc_typeck/check/intrinsic.rs12
-rw-r--r--src/libstd/panicking.rs12
-rw-r--r--src/test/ui/consts/const-eval/const_caller_location.rs23
-rw-r--r--src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs5
-rw-r--r--src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs9
25 files changed, 331 insertions, 145 deletions
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<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,
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<Symbol>,
-        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<u64>;
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<Symbol>,
-        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<hir::Mutability>,
     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(msg.into())?;
+            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 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);
+}