]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs
Rollup merge of #81154 - dylni:improve-design-of-assert-len, r=KodrAus
[rust.git] / compiler / rustc_mir / src / interpret / intrinsics / caller_location.rs
1 use std::convert::TryFrom;
2
3 use rustc_hir::lang_items::LangItem;
4 use rustc_middle::mir::TerminatorKind;
5 use rustc_middle::ty::subst::Subst;
6 use rustc_span::{Span, Symbol};
7 use rustc_target::abi::LayoutOf;
8
9 use crate::interpret::{
10     intrinsics::{InterpCx, Machine},
11     MPlaceTy, MemoryKind, Scalar,
12 };
13
14 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
15     /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a
16     /// frame which is not `#[track_caller]`.
17     crate fn find_closest_untracked_caller_location(&self) -> Span {
18         for frame in self.stack().iter().rev() {
19             debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance);
20
21             // Assert that the frame we look at is actually executing code currently
22             // (`loc` is `Err` when we are unwinding and the frame does not require cleanup).
23             let loc = frame.loc.unwrap();
24
25             // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all
26             // (such as `box`). Use the normal span by default.
27             let mut source_info = *frame.body.source_info(loc);
28
29             // If this is a `Call` terminator, use the `fn_span` instead.
30             let block = &frame.body.basic_blocks()[loc.block];
31             if loc.statement_index == block.statements.len() {
32                 debug!(
33                     "find_closest_untracked_caller_location: got terminator {:?} ({:?})",
34                     block.terminator(),
35                     block.terminator().kind
36                 );
37                 if let TerminatorKind::Call { fn_span, .. } = block.terminator().kind {
38                     source_info.span = fn_span;
39                 }
40             }
41
42             // Walk up the `SourceScope`s, in case some of them are from MIR inlining.
43             // If so, the starting `source_info.span` is in the innermost inlined
44             // function, and will be replaced with outer callsite spans as long
45             // as the inlined functions were `#[track_caller]`.
46             loop {
47                 let scope_data = &frame.body.source_scopes[source_info.scope];
48
49                 if let Some((callee, callsite_span)) = scope_data.inlined {
50                     // Stop inside the most nested non-`#[track_caller]` function,
51                     // before ever reaching its caller (which is irrelevant).
52                     if !callee.def.requires_caller_location(*self.tcx) {
53                         return source_info.span;
54                     }
55                     source_info.span = callsite_span;
56                 }
57
58                 // Skip past all of the parents with `inlined: None`.
59                 match scope_data.inlined_parent_scope {
60                     Some(parent) => source_info.scope = parent,
61                     None => break,
62                 }
63             }
64
65             // Stop inside the most nested non-`#[track_caller]` function,
66             // before ever reaching its caller (which is irrelevant).
67             if !frame.instance.def.requires_caller_location(*self.tcx) {
68                 return source_info.span;
69             }
70         }
71
72         bug!("no non-`#[track_caller]` frame found")
73     }
74
75     /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.
76     crate fn alloc_caller_location(
77         &mut self,
78         filename: Symbol,
79         line: u32,
80         col: u32,
81     ) -> MPlaceTy<'tcx, M::PointerTag> {
82         let file = self.allocate_str(&filename.as_str(), MemoryKind::CallerLocation);
83         let line = Scalar::from_u32(line);
84         let col = Scalar::from_u32(col);
85
86         // Allocate memory for `CallerLocation` struct.
87         let loc_ty = self
88             .tcx
89             .type_of(self.tcx.require_lang_item(LangItem::PanicLocation, None))
90             .subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_erased.into()].iter()));
91         let loc_layout = self.layout_of(loc_ty).unwrap();
92         let location = self.allocate(loc_layout, MemoryKind::CallerLocation);
93
94         // Initialize fields.
95         self.write_immediate(file.to_ref(), &self.mplace_field(&location, 0).unwrap().into())
96             .expect("writing to memory we just allocated cannot fail");
97         self.write_scalar(line, &self.mplace_field(&location, 1).unwrap().into())
98             .expect("writing to memory we just allocated cannot fail");
99         self.write_scalar(col, &self.mplace_field(&location, 2).unwrap().into())
100             .expect("writing to memory we just allocated cannot fail");
101
102         location
103     }
104
105     crate fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) {
106         let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
107         let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
108         (
109             Symbol::intern(&caller.file.name.to_string()),
110             u32::try_from(caller.line).unwrap(),
111             u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(),
112         )
113     }
114
115     pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::PointerTag> {
116         let (file, line, column) = self.location_triple_for_span(span);
117         self.alloc_caller_location(file, line, column)
118     }
119 }