interpret: call caller_location logic the same way codegen does, and share some code
This commit is contained in:
parent
615d0f2400
commit
351d532a27
12 changed files with 173 additions and 176 deletions
|
@ -433,16 +433,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
|
||||||
// Note: must be kept in sync with get_caller_location from cg_ssa
|
// 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> {
|
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| {
|
let span_to_caller_location = |fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span| {
|
||||||
use rustc_session::RemapFileNameExt;
|
let const_loc = fx.tcx.span_as_caller_location(span);
|
||||||
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,
|
|
||||||
));
|
|
||||||
crate::constant::codegen_const_value(fx, const_loc, fx.tcx.caller_location_ty())
|
crate::constant::codegen_const_value(fx, const_loc, fx.tcx.caller_location_ty())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1454,14 +1454,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
let tcx = bx.tcx();
|
let tcx = bx.tcx();
|
||||||
|
|
||||||
let mut span_to_caller_location = |span: Span| {
|
let mut span_to_caller_location = |span: Span| {
|
||||||
use rustc_session::RemapFileNameExt;
|
let const_loc = tcx.span_as_caller_location(span);
|
||||||
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,
|
|
||||||
));
|
|
||||||
OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty())
|
OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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
|
/// 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
|
/// 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.
|
/// 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>,
|
tcx: TyCtxt<'tcx>,
|
||||||
root_span: Span,
|
root_span: Span,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
|
|
@ -4,6 +4,7 @@ use rustc_middle::mir;
|
||||||
use rustc_middle::mir::interpret::PointerArithmetic;
|
use rustc_middle::mir::interpret::PointerArithmetic;
|
||||||
use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
|
use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
|
||||||
use rustc_middle::ty::{self, TyCtxt};
|
use rustc_middle::ty::{self, TyCtxt};
|
||||||
|
use rustc_span::Span;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
@ -181,6 +182,24 @@ impl interpret::MayLeak for ! {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
|
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.
|
/// "Intercept" a function call, because we have something special to do for it.
|
||||||
/// All `#[rustc_do_not_const_check]` functions should be hooked here.
|
/// 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
|
/// If this returns `Some` function, which may be `instance` or a different function with
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
// Not in interpret to make sure we do not use private implementation details
|
// Not in interpret to make sure we do not use private implementation details
|
||||||
|
|
||||||
use crate::errors::MaxNumNodesInConstErr;
|
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;
|
||||||
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
|
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
|
||||||
use rustc_middle::query::TyCtxtAt;
|
use rustc_middle::query::TyCtxtAt;
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
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 error;
|
||||||
mod eval_queries;
|
mod eval_queries;
|
||||||
|
@ -20,20 +20,6 @@ pub use fn_queries::*;
|
||||||
pub use machine::*;
|
pub use machine::*;
|
||||||
pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value};
|
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.
|
// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
|
||||||
const VALTREE_MAX_NODES: usize = 100000;
|
const VALTREE_MAX_NODES: usize = 100000;
|
||||||
|
|
||||||
|
|
|
@ -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)]
|
#[inline(always)]
|
||||||
pub fn layout_of_local(
|
pub fn layout_of_local(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -22,8 +22,6 @@ use super::{
|
||||||
|
|
||||||
use crate::fluent_generated as fluent;
|
use crate::fluent_generated as fluent;
|
||||||
|
|
||||||
mod caller_location;
|
|
||||||
|
|
||||||
fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> {
|
fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> {
|
||||||
let size = match kind {
|
let size = match kind {
|
||||||
Primitive::Int(integer, _) => integer.size(),
|
Primitive::Int(integer, _) => integer.size(),
|
||||||
|
@ -130,8 +128,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
match intrinsic_name {
|
match intrinsic_name {
|
||||||
sym::caller_location => {
|
sym::caller_location => {
|
||||||
let span = self.find_closest_untracked_caller_location();
|
let span = self.find_closest_untracked_caller_location();
|
||||||
let location = self.alloc_caller_location_for_span(span);
|
let val = self.tcx.span_as_caller_location(span);
|
||||||
self.write_immediate(location.to_ref(self), dest)?;
|
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 => {
|
sym::min_align_of_val | sym::size_of_val => {
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -49,7 +49,7 @@ pub fn provide(providers: &mut Providers) {
|
||||||
const_eval::provide(providers);
|
const_eval::provide(providers);
|
||||||
providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider;
|
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.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| {
|
providers.eval_to_valtree = |tcx, param_env_and_value| {
|
||||||
let (param_env, raw) = param_env_and_value.into_parts();
|
let (param_env, raw) = param_env_and_value.into_parts();
|
||||||
const_eval::eval_to_valtree(tcx, param_env, raw)
|
const_eval::eval_to_valtree(tcx, param_env, raw)
|
||||||
|
|
63
compiler/rustc_const_eval/src/util/caller_location.rs
Normal file
63
compiler/rustc_const_eval/src/util/caller_location.rs
Normal file
|
@ -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))
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
use rustc_middle::mir;
|
use rustc_middle::mir;
|
||||||
|
|
||||||
mod alignment;
|
mod alignment;
|
||||||
|
pub(crate) mod caller_location;
|
||||||
mod check_validity_requirement;
|
mod check_validity_requirement;
|
||||||
mod compare_types;
|
mod compare_types;
|
||||||
mod type_name;
|
mod type_name;
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::fmt::{self, Debug, Display, Formatter};
|
||||||
use rustc_hir;
|
use rustc_hir;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::{self as hir};
|
use rustc_hir::{self as hir};
|
||||||
|
use rustc_session::RemapFileNameExt;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_target::abi::{HasDataLayout, Size};
|
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,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue