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