Rollup merge of #117317 - RalfJung:track-caller, r=oli-obk
share some track_caller logic between interpret and codegen Also move the code that implements the track_caller intrinsics out of the core interpreter engine -- it's just a helper creating a const-allocation, doesn't need to be part of the interpreter core.
This commit is contained in:
commit
73100d8e93
@ -430,47 +430,11 @@ 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,
|
||||
));
|
||||
crate::constant::codegen_const_value(fx, const_loc, fx.tcx.caller_location_ty())
|
||||
};
|
||||
|
||||
// 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 = &self.mir.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 span_to_caller_location(self, 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,
|
||||
}
|
||||
}
|
||||
|
||||
// No inlined `SourceScope`s, or all of them were `#[track_caller]`.
|
||||
self.caller_location.unwrap_or_else(|| span_to_caller_location(self, source_info.span))
|
||||
pub(crate) fn get_caller_location(&mut self, source_info: mir::SourceInfo) -> CValue<'tcx> {
|
||||
self.mir.caller_location_span(source_info, self.caller_location, self.tcx, |span| {
|
||||
let const_loc = self.tcx.span_as_caller_location(span);
|
||||
crate::constant::codegen_const_value(self, const_loc, self.tcx.caller_location_ty())
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn anonymous_str(&mut self, msg: &str) -> Value {
|
||||
|
@ -1449,47 +1449,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
fn get_caller_location(
|
||||
&mut self,
|
||||
bx: &mut Bx,
|
||||
mut source_info: mir::SourceInfo,
|
||||
source_info: mir::SourceInfo,
|
||||
) -> OperandRef<'tcx, Bx::Value> {
|
||||
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,
|
||||
));
|
||||
self.mir.caller_location_span(source_info, self.caller_location, bx.tcx(), |span: Span| {
|
||||
let const_loc = bx.tcx().span_as_caller_location(span);
|
||||
OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty())
|
||||
};
|
||||
|
||||
// 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 = &self.mir.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(tcx) {
|
||||
return span_to_caller_location(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,
|
||||
}
|
||||
}
|
||||
|
||||
// No inlined `SourceScope`s, or all of them were `#[track_caller]`.
|
||||
self.caller_location.unwrap_or_else(|| span_to_caller_location(source_info.span))
|
||||
})
|
||||
}
|
||||
|
||||
fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> {
|
||||
|
@ -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>,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -595,6 +595,50 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
let caller_location = if frame.instance.def.requires_caller_location(*self.tcx) {
|
||||
// We use `Err(())` as indication that we should continue up the call stack since
|
||||
// this is a `#[track_caller]` function.
|
||||
Some(Err(()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Ok(span) =
|
||||
frame.body.caller_location_span(source_info, caller_location, *self.tcx, Ok)
|
||||
{
|
||||
return span;
|
||||
}
|
||||
}
|
||||
|
||||
span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn layout_of_local(
|
||||
&self,
|
||||
|
@ -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 => {
|
||||
|
@ -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);
|
||||
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)
|
||||
|
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;
|
||||
|
||||
mod alignment;
|
||||
pub(crate) mod caller_location;
|
||||
mod check_validity_requirement;
|
||||
mod compare_types;
|
||||
mod type_name;
|
||||
|
@ -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};
|
||||
|
||||
@ -579,3 +580,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,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -578,6 +578,40 @@ impl<'tcx> Body<'tcx> {
|
||||
pub fn is_custom_mir(&self) -> bool {
|
||||
self.injection_phase.is_some()
|
||||
}
|
||||
|
||||
/// For a `Location` in this scope, determine what the "caller location" at that point is. This
|
||||
/// is interesting because of inlining: the `#[track_caller]` attribute of inlined functions
|
||||
/// must be honored. Falls back to the `tracked_caller` value for `#[track_caller]` functions,
|
||||
/// or the function's scope.
|
||||
pub fn caller_location_span<T>(
|
||||
&self,
|
||||
mut source_info: SourceInfo,
|
||||
caller_location: Option<T>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
from_span: impl FnOnce(Span) -> T,
|
||||
) -> T {
|
||||
loop {
|
||||
let scope_data = &self.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(tcx) {
|
||||
return from_span(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,
|
||||
}
|
||||
}
|
||||
|
||||
// No inlined `SourceScope`s, or all of them were `#[track_caller]`.
|
||||
caller_location.unwrap_or_else(|| from_span(source_info.span))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||
|
Loading…
x
Reference in New Issue
Block a user