about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2023-10-28 15:39:54 +0200
committerRalf Jung <post@ralfj.de>2023-10-28 15:40:03 +0200
commit351d532a27c56f7af82feaa7d612c412fd5c221c (patch)
tree5c5e4c3a5461e0f429571dd89d912bff9956a082
parent615d0f2400428eed3b086ca5332369ec150143d6 (diff)
downloadrust-351d532a27c56f7af82feaa7d612c412fd5c221c.tar.gz
rust-351d532a27c56f7af82feaa7d612c412fd5c221c.zip
interpret: call caller_location logic the same way codegen does, and share some code
-rw-r--r--compiler/rustc_codegen_cranelift/src/common.rs11
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs9
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs19
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs18
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs62
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs8
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs136
-rw-r--r--compiler/rustc_const_eval/src/lib.rs2
-rw-r--r--compiler/rustc_const_eval/src/util/caller_location.rs63
-rw-r--r--compiler/rustc_const_eval/src/util/mod.rs1
-rw-r--r--compiler/rustc_middle/src/mir/consts.rs18
12 files changed, 173 insertions, 176 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs
index 9771f44f62c..38dba016c23 100644
--- a/compiler/rustc_codegen_cranelift/src/common.rs
+++ b/compiler/rustc_codegen_cranelift/src/common.rs
@@ -433,16 +433,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
     // Note: must be kept in sync with get_caller_location from cg_ssa
     pub(crate) fn get_caller_location(&mut self, mut source_info: mir::SourceInfo) -> CValue<'tcx> {
         let span_to_caller_location = |fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span| {
-            use rustc_session::RemapFileNameExt;
-            let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
-            let caller = fx.tcx.sess.source_map().lookup_char_pos(topmost.lo());
-            let const_loc = fx.tcx.const_caller_location((
-                rustc_span::symbol::Symbol::intern(
-                    &caller.file.name.for_codegen(&fx.tcx.sess).to_string_lossy(),
-                ),
-                caller.line as u32,
-                caller.col_display as u32 + 1,
-            ));
+            let const_loc = fx.tcx.span_as_caller_location(span);
             crate::constant::codegen_const_value(fx, const_loc, fx.tcx.caller_location_ty())
         };
 
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index caade768795..cbe3c736896 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1454,14 +1454,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         let tcx = bx.tcx();
 
         let mut span_to_caller_location = |span: Span| {
-            use rustc_session::RemapFileNameExt;
-            let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
-            let caller = tcx.sess.source_map().lookup_char_pos(topmost.lo());
-            let const_loc = tcx.const_caller_location((
-                Symbol::intern(&caller.file.name.for_codegen(self.cx.sess()).to_string_lossy()),
-                caller.line as u32,
-                caller.col_display as u32 + 1,
-            ));
+            let const_loc = tcx.span_as_caller_location(span);
             OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty())
         };
 
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 46e00d0f176..669838308a5 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -90,7 +90,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
 /// that inform us about the generic bounds of the constant. E.g., using an associated constant
 /// of a function's generic parameter will require knowledge about the bounds on the generic
 /// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument.
-pub(super) fn mk_eval_cx<'mir, 'tcx>(
+pub(crate) fn mk_eval_cx<'mir, 'tcx>(
     tcx: TyCtxt<'tcx>,
     root_span: Span,
     param_env: ty::ParamEnv<'tcx>,
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 0d0ebe6f390..4b447229c5f 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -4,6 +4,7 @@ use rustc_middle::mir;
 use rustc_middle::mir::interpret::PointerArithmetic;
 use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
 use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::Span;
 use std::borrow::Borrow;
 use std::hash::Hash;
 use std::ops::ControlFlow;
@@ -181,6 +182,24 @@ impl interpret::MayLeak for ! {
 }
 
 impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
+    fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) {
+        let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
+        let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
+
+        use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt};
+        (
+            Symbol::intern(
+                &caller
+                    .file
+                    .name
+                    .for_scope(&self.tcx.sess, RemapPathScopeComponents::DIAGNOSTICS)
+                    .to_string_lossy(),
+            ),
+            u32::try_from(caller.line).unwrap(),
+            u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(),
+        )
+    }
+
     /// "Intercept" a function call, because we have something special to do for it.
     /// All `#[rustc_do_not_const_check]` functions should be hooked here.
     /// If this returns `Some` function, which may be `instance` or a different function with
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index bcbe996be7d..d6ee6975cdd 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -1,12 +1,12 @@
 // Not in interpret to make sure we do not use private implementation details
 
 use crate::errors::MaxNumNodesInConstErr;
-use crate::interpret::{intern_const_alloc_recursive, InternKind, InterpCx, Scalar};
+use crate::interpret::InterpCx;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
 use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
+use rustc_span::source_map::DUMMY_SP;
 
 mod error;
 mod eval_queries;
@@ -20,20 +20,6 @@ pub use fn_queries::*;
 pub use machine::*;
 pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value};
 
-pub(crate) fn const_caller_location(
-    tcx: TyCtxt<'_>,
-    (file, line, col): (Symbol, u32, u32),
-) -> mir::ConstValue<'_> {
-    trace!("const_caller_location: {}:{}:{}", file, line, col);
-    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No);
-
-    let loc_place = ecx.alloc_caller_location(file, line, col);
-    if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
-        bug!("intern_const_alloc_recursive should not error in this case")
-    }
-    mir::ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx))
-}
-
 // We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
 const VALTREE_MAX_NODES: usize = 100000;
 
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index ec0af79459c..f3c19495a61 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -595,6 +595,68 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         }
     }
 
+    /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a
+    /// frame which is not `#[track_caller]`. This is the fancy version of `cur_span`.
+    pub(crate) fn find_closest_untracked_caller_location(&self) -> Span {
+        for frame in self.stack().iter().rev() {
+            debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance);
+
+            // Assert that the frame we look at is actually executing code currently
+            // (`loc` is `Right` when we are unwinding and the frame does not require cleanup).
+            let loc = frame.loc.left().unwrap();
+
+            // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all
+            // (such as `box`). Use the normal span by default.
+            let mut source_info = *frame.body.source_info(loc);
+
+            // If this is a `Call` terminator, use the `fn_span` instead.
+            let block = &frame.body.basic_blocks[loc.block];
+            if loc.statement_index == block.statements.len() {
+                debug!(
+                    "find_closest_untracked_caller_location: got terminator {:?} ({:?})",
+                    block.terminator(),
+                    block.terminator().kind,
+                );
+                if let mir::TerminatorKind::Call { fn_span, .. } = block.terminator().kind {
+                    source_info.span = fn_span;
+                }
+            }
+
+            // Note: this must be kept in sync with get_caller_location from cg_ssa.
+
+            // Walk up the `SourceScope`s, in case some of them are from MIR inlining.
+            // If so, the starting `source_info.span` is in the innermost inlined
+            // function, and will be replaced with outer callsite spans as long
+            // as the inlined functions were `#[track_caller]`.
+            loop {
+                let scope_data = &frame.body.source_scopes[source_info.scope];
+
+                if let Some((callee, callsite_span)) = scope_data.inlined {
+                    // Stop inside the most nested non-`#[track_caller]` function,
+                    // before ever reaching its caller (which is irrelevant).
+                    if !callee.def.requires_caller_location(*self.tcx) {
+                        return source_info.span;
+                    }
+                    source_info.span = callsite_span;
+                }
+
+                // Skip past all of the parents with `inlined: None`.
+                match scope_data.inlined_parent_scope {
+                    Some(parent) => source_info.scope = parent,
+                    None => break,
+                }
+            }
+
+            // Stop inside the most nested non-`#[track_caller]` function,
+            // before ever reaching its caller (which is irrelevant).
+            if !frame.instance.def.requires_caller_location(*self.tcx) {
+                return source_info.span;
+            }
+        }
+
+        span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found")
+    }
+
     #[inline(always)]
     pub fn layout_of_local(
         &self,
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index c97207a61ac..8fd04a68791 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -22,8 +22,6 @@ use super::{
 
 use crate::fluent_generated as fluent;
 
-mod caller_location;
-
 fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> {
     let size = match kind {
         Primitive::Int(integer, _) => integer.size(),
@@ -130,8 +128,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         match intrinsic_name {
             sym::caller_location => {
                 let span = self.find_closest_untracked_caller_location();
-                let location = self.alloc_caller_location_for_span(span);
-                self.write_immediate(location.to_ref(self), dest)?;
+                let val = self.tcx.span_as_caller_location(span);
+                let val =
+                    self.const_val_to_op(val, self.tcx.caller_location_ty(), Some(dest.layout))?;
+                self.copy_op(&val, dest, /* allow_transmute */ false)?;
             }
 
             sym::min_align_of_val | sym::size_of_val => {
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
deleted file mode 100644
index 16b7decf9c4..00000000000
--- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-use rustc_ast::Mutability;
-use rustc_hir::lang_items::LangItem;
-use rustc_middle::mir::TerminatorKind;
-use rustc_middle::ty::layout::LayoutOf;
-use rustc_span::{Span, Symbol};
-
-use crate::interpret::{
-    intrinsics::{InterpCx, Machine},
-    MPlaceTy, MemoryKind, Scalar,
-};
-
-impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
-    /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a
-    /// frame which is not `#[track_caller]`.
-    pub(crate) fn find_closest_untracked_caller_location(&self) -> Span {
-        for frame in self.stack().iter().rev() {
-            debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance);
-
-            // Assert that the frame we look at is actually executing code currently
-            // (`loc` is `Right` when we are unwinding and the frame does not require cleanup).
-            let loc = frame.loc.left().unwrap();
-
-            // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all
-            // (such as `box`). Use the normal span by default.
-            let mut source_info = *frame.body.source_info(loc);
-
-            // If this is a `Call` terminator, use the `fn_span` instead.
-            let block = &frame.body.basic_blocks[loc.block];
-            if loc.statement_index == block.statements.len() {
-                debug!(
-                    "find_closest_untracked_caller_location: got terminator {:?} ({:?})",
-                    block.terminator(),
-                    block.terminator().kind
-                );
-                if let TerminatorKind::Call { fn_span, .. } = block.terminator().kind {
-                    source_info.span = fn_span;
-                }
-            }
-
-            // Walk up the `SourceScope`s, in case some of them are from MIR inlining.
-            // If so, the starting `source_info.span` is in the innermost inlined
-            // function, and will be replaced with outer callsite spans as long
-            // as the inlined functions were `#[track_caller]`.
-            loop {
-                let scope_data = &frame.body.source_scopes[source_info.scope];
-
-                if let Some((callee, callsite_span)) = scope_data.inlined {
-                    // Stop inside the most nested non-`#[track_caller]` function,
-                    // before ever reaching its caller (which is irrelevant).
-                    if !callee.def.requires_caller_location(*self.tcx) {
-                        return source_info.span;
-                    }
-                    source_info.span = callsite_span;
-                }
-
-                // Skip past all of the parents with `inlined: None`.
-                match scope_data.inlined_parent_scope {
-                    Some(parent) => source_info.scope = parent,
-                    None => break,
-                }
-            }
-
-            // Stop inside the most nested non-`#[track_caller]` function,
-            // before ever reaching its caller (which is irrelevant).
-            if !frame.instance.def.requires_caller_location(*self.tcx) {
-                return source_info.span;
-            }
-        }
-
-        span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found")
-    }
-
-    /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.
-    pub(crate) fn alloc_caller_location(
-        &mut self,
-        filename: Symbol,
-        line: u32,
-        col: u32,
-    ) -> MPlaceTy<'tcx, M::Provenance> {
-        let loc_details = self.tcx.sess.opts.unstable_opts.location_detail;
-        // This can fail if rustc runs out of memory right here. Trying to emit an error would be
-        // pointless, since that would require allocating more memory than these short strings.
-        let file = if loc_details.file {
-            self.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not)
-                .unwrap()
-        } else {
-            // FIXME: This creates a new allocation each time. It might be preferable to
-            // perform this allocation only once, and re-use the `MPlaceTy`.
-            // See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398
-            self.allocate_str("<redacted>", MemoryKind::CallerLocation, Mutability::Not).unwrap()
-        };
-        let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) };
-        let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) };
-
-        // Allocate memory for `CallerLocation` struct.
-        let loc_ty = self
-            .tcx
-            .type_of(self.tcx.require_lang_item(LangItem::PanicLocation, None))
-            .instantiate(*self.tcx, self.tcx.mk_args(&[self.tcx.lifetimes.re_erased.into()]));
-        let loc_layout = self.layout_of(loc_ty).unwrap();
-        let location = self.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();
-
-        // Initialize fields.
-        self.write_immediate(file.to_ref(self), &self.project_field(&location, 0).unwrap())
-            .expect("writing to memory we just allocated cannot fail");
-        self.write_scalar(line, &self.project_field(&location, 1).unwrap())
-            .expect("writing to memory we just allocated cannot fail");
-        self.write_scalar(col, &self.project_field(&location, 2).unwrap())
-            .expect("writing to memory we just allocated cannot fail");
-
-        location
-    }
-
-    pub(crate) fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) {
-        let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
-        let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
-
-        use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt};
-        (
-            Symbol::intern(
-                &caller
-                    .file
-                    .name
-                    .for_scope(&self.tcx.sess, RemapPathScopeComponents::DIAGNOSTICS)
-                    .to_string_lossy(),
-            ),
-            u32::try_from(caller.line).unwrap(),
-            u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(),
-        )
-    }
-
-    pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::Provenance> {
-        let (file, line, column) = self.location_triple_for_span(span);
-        self.alloc_caller_location(file, line, column)
-    }
-}
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index 1fd5723f277..7fc87ed8881 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -49,7 +49,7 @@ pub fn provide(providers: &mut Providers) {
     const_eval::provide(providers);
     providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider;
     providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider;
-    providers.const_caller_location = const_eval::const_caller_location;
+    providers.const_caller_location = util::caller_location::const_caller_location_provider;
     providers.eval_to_valtree = |tcx, param_env_and_value| {
         let (param_env, raw) = param_env_and_value.into_parts();
         const_eval::eval_to_valtree(tcx, param_env, raw)
diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs
new file mode 100644
index 00000000000..d67863d75d6
--- /dev/null
+++ b/compiler/rustc_const_eval/src/util/caller_location.rs
@@ -0,0 +1,63 @@
+use rustc_hir::LangItem;
+use rustc_middle::mir;
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
+use rustc_type_ir::Mutability;
+
+use crate::const_eval::{mk_eval_cx, CanAccessStatics, CompileTimeEvalContext};
+use crate::interpret::*;
+
+/// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.
+fn alloc_caller_location<'mir, 'tcx>(
+    ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
+    filename: Symbol,
+    line: u32,
+    col: u32,
+) -> MPlaceTy<'tcx> {
+    let loc_details = ecx.tcx.sess.opts.unstable_opts.location_detail;
+    // This can fail if rustc runs out of memory right here. Trying to emit an error would be
+    // pointless, since that would require allocating more memory than these short strings.
+    let file = if loc_details.file {
+        ecx.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not).unwrap()
+    } else {
+        // FIXME: This creates a new allocation each time. It might be preferable to
+        // perform this allocation only once, and re-use the `MPlaceTy`.
+        // See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398
+        ecx.allocate_str("<redacted>", MemoryKind::CallerLocation, Mutability::Not).unwrap()
+    };
+    let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) };
+    let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) };
+
+    // Allocate memory for `CallerLocation` struct.
+    let loc_ty = ecx
+        .tcx
+        .type_of(ecx.tcx.require_lang_item(LangItem::PanicLocation, None))
+        .instantiate(*ecx.tcx, ecx.tcx.mk_args(&[ecx.tcx.lifetimes.re_erased.into()]));
+    let loc_layout = ecx.layout_of(loc_ty).unwrap();
+    let location = ecx.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();
+
+    // Initialize fields.
+    ecx.write_immediate(file.to_ref(ecx), &ecx.project_field(&location, 0).unwrap())
+        .expect("writing to memory we just allocated cannot fail");
+    ecx.write_scalar(line, &ecx.project_field(&location, 1).unwrap())
+        .expect("writing to memory we just allocated cannot fail");
+    ecx.write_scalar(col, &ecx.project_field(&location, 2).unwrap())
+        .expect("writing to memory we just allocated cannot fail");
+
+    location
+}
+
+pub(crate) fn const_caller_location_provider(
+    tcx: TyCtxt<'_>,
+    (file, line, col): (Symbol, u32, u32),
+) -> mir::ConstValue<'_> {
+    trace!("const_caller_location: {}:{}:{}", file, line, col);
+    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No);
+
+    let loc_place = alloc_caller_location(&mut ecx, file, line, col);
+    if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
+        bug!("intern_const_alloc_recursive should not error in this case")
+    }
+    mir::ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx))
+}
diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs
index 040b3071e6f..1e58bd645cd 100644
--- a/compiler/rustc_const_eval/src/util/mod.rs
+++ b/compiler/rustc_const_eval/src/util/mod.rs
@@ -1,6 +1,7 @@
 use rustc_middle::mir;
 
 mod alignment;
+pub(crate) mod caller_location;
 mod check_validity_requirement;
 mod compare_types;
 mod type_name;
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
index 4e429f316e8..47865bae0d2 100644
--- a/compiler/rustc_middle/src/mir/consts.rs
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -3,6 +3,7 @@ use std::fmt::{self, Debug, Display, Formatter};
 use rustc_hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::{self as hir};
+use rustc_session::RemapFileNameExt;
 use rustc_span::Span;
 use rustc_target::abi::{HasDataLayout, Size};
 
@@ -529,3 +530,20 @@ impl<'tcx> Display for Const<'tcx> {
         }
     }
 }
+
+///////////////////////////////////////////////////////////////////////////
+/// Const-related utilities
+
+impl<'tcx> TyCtxt<'tcx> {
+    pub fn span_as_caller_location(self, span: Span) -> ConstValue<'tcx> {
+        let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
+        let caller = self.sess.source_map().lookup_char_pos(topmost.lo());
+        self.const_caller_location((
+            rustc_span::symbol::Symbol::intern(
+                &caller.file.name.for_codegen(&self.sess).to_string_lossy(),
+            ),
+            caller.line as u32,
+            caller.col_display as u32 + 1,
+        ))
+    }
+}