rustc_const_eval/util/
caller_location.rs

1use rustc_abi::FieldIdx;
2use rustc_hir::LangItem;
3use rustc_middle::ty::layout::LayoutOf;
4use rustc_middle::ty::{self, TyCtxt};
5use rustc_middle::{bug, mir};
6use rustc_span::Symbol;
7use tracing::trace;
8
9use crate::const_eval::{CanAccessMutGlobal, CompileTimeInterpCx, mk_eval_cx_to_read_const_val};
10use crate::interpret::*;
11
12/// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.
13fn alloc_caller_location<'tcx>(
14    ecx: &mut CompileTimeInterpCx<'tcx>,
15    filename: Symbol,
16    line: u32,
17    col: u32,
18) -> MPlaceTy<'tcx> {
19    // Ensure that the filename itself does not contain nul bytes.
20    // This isn't possible via POSIX or Windows, but we should ensure no one
21    // ever does such a thing.
22    assert!(!filename.as_str().as_bytes().contains(&0));
23
24    let loc_details = ecx.tcx.sess.opts.unstable_opts.location_detail;
25    let file_wide_ptr = {
26        let filename = if loc_details.file { filename.as_str() } else { "<redacted>" };
27        let filename_with_nul = filename.to_owned() + "\0";
28        // This can fail if rustc runs out of memory right here. Trying to emit an error would be
29        // pointless, since that would require allocating more memory than these short strings.
30        let file_ptr = ecx.allocate_bytes_dedup(filename_with_nul.as_bytes()).unwrap();
31        Immediate::new_slice(file_ptr.into(), filename_with_nul.len().try_into().unwrap(), ecx)
32    };
33    let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) };
34    let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) };
35
36    // Allocate memory for `CallerLocation` struct.
37    let loc_ty = ecx
38        .tcx
39        .type_of(ecx.tcx.require_lang_item(LangItem::PanicLocation, ecx.tcx.span))
40        .instantiate(*ecx.tcx, ecx.tcx.mk_args(&[ecx.tcx.lifetimes.re_erased.into()]));
41    let loc_layout = ecx.layout_of(loc_ty).unwrap();
42    let location = ecx.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();
43
44    // Initialize fields.
45    ecx.write_immediate(
46        file_wide_ptr,
47        &ecx.project_field(&location, FieldIdx::from_u32(0)).unwrap(),
48    )
49    .expect("writing to memory we just allocated cannot fail");
50    ecx.write_scalar(line, &ecx.project_field(&location, FieldIdx::from_u32(1)).unwrap())
51        .expect("writing to memory we just allocated cannot fail");
52    ecx.write_scalar(col, &ecx.project_field(&location, FieldIdx::from_u32(2)).unwrap())
53        .expect("writing to memory we just allocated cannot fail");
54
55    location
56}
57
58pub(crate) fn const_caller_location_provider(
59    tcx: TyCtxt<'_>,
60    file: Symbol,
61    line: u32,
62    col: u32,
63) -> mir::ConstValue<'_> {
64    trace!("const_caller_location: {}:{}:{}", file, line, col);
65    let mut ecx = mk_eval_cx_to_read_const_val(
66        tcx,
67        rustc_span::DUMMY_SP, // FIXME: use a proper span here?
68        ty::TypingEnv::fully_monomorphized(),
69        CanAccessMutGlobal::No,
70    );
71
72    let loc_place = alloc_caller_location(&mut ecx, file, line, col);
73    if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
74        bug!("intern_const_alloc_recursive should not error in this case")
75    }
76    mir::ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx))
77}