]> git.lizzy.rs Git - rust.git/blob - src/stacked_borrows/diagnostics.rs
Auto merge of #2430 - RalfJung:no-global-wrapper, r=RalfJung
[rust.git] / src / stacked_borrows / diagnostics.rs
1 use smallvec::SmallVec;
2
3 use rustc_middle::mir::interpret::{AllocId, AllocRange};
4 use rustc_span::{Span, SpanData};
5 use rustc_target::abi::Size;
6
7 use crate::helpers::CurrentSpan;
8 use crate::stacked_borrows::{err_sb_ub, AccessKind};
9 use crate::*;
10
11 use rustc_middle::mir::interpret::InterpError;
12
13 #[derive(Clone, Debug)]
14 pub struct AllocHistory {
15     // The time tags can be compressed down to one bit per event, by just storing a Vec<u8>
16     // where each bit is set to indicate if the event was a creation or a retag
17     current_time: usize,
18     creations: smallvec::SmallVec<[Event; 2]>,
19     invalidations: smallvec::SmallVec<[Event; 1]>,
20     protectors: smallvec::SmallVec<[Protection; 1]>,
21 }
22
23 #[derive(Clone, Debug)]
24 struct Protection {
25     orig_tag: SbTag,
26     tag: SbTag,
27     span: Span,
28 }
29
30 #[derive(Clone, Debug)]
31 struct Event {
32     parent: Option<SbTag>,
33     tag: SbTag,
34     range: AllocRange,
35     span: Span,
36 }
37
38 pub enum TagHistory {
39     Tagged {
40         tag: SbTag,
41         created: (AllocRange, SpanData),
42         invalidated: Option<(AllocRange, SpanData)>,
43         protected: Option<(SbTag, SpanData, SpanData)>,
44     },
45 }
46
47 impl AllocHistory {
48     pub fn new() -> Self {
49         Self {
50             current_time: 0,
51             creations: SmallVec::new(),
52             invalidations: SmallVec::new(),
53             protectors: SmallVec::new(),
54         }
55     }
56
57     pub fn log_creation(
58         &mut self,
59         parent: Option<SbTag>,
60         tag: SbTag,
61         range: AllocRange,
62         current_span: &mut CurrentSpan<'_, '_, '_>,
63     ) {
64         let span = current_span.get();
65         self.creations.push(Event { parent, tag, range, span });
66         self.current_time += 1;
67     }
68
69     pub fn log_invalidation(
70         &mut self,
71         tag: SbTag,
72         range: AllocRange,
73         current_span: &mut CurrentSpan<'_, '_, '_>,
74     ) {
75         let span = current_span.get();
76         self.invalidations.push(Event { parent: None, tag, range, span });
77         self.current_time += 1;
78     }
79
80     pub fn log_protector(
81         &mut self,
82         orig_tag: SbTag,
83         tag: SbTag,
84         current_span: &mut CurrentSpan<'_, '_, '_>,
85     ) {
86         let span = current_span.get();
87         self.protectors.push(Protection { orig_tag, tag, span });
88         self.current_time += 1;
89     }
90
91     pub fn get_logs_relevant_to(
92         &self,
93         tag: SbTag,
94         protector_tag: Option<SbTag>,
95     ) -> Option<TagHistory> {
96         let protected = protector_tag
97             .and_then(|protector| {
98                 self.protectors.iter().find_map(|protection| {
99                     if protection.tag == protector {
100                         Some((protection.orig_tag, protection.span.data()))
101                     } else {
102                         None
103                     }
104                 })
105             })
106             .and_then(|(tag, call_span)| {
107                 self.creations.iter().rev().find_map(|event| {
108                     if event.tag == tag {
109                         Some((event.parent?, event.span.data(), call_span))
110                     } else {
111                         None
112                     }
113                 })
114             });
115
116         let get_matching = |events: &[Event]| {
117             events.iter().rev().find_map(|event| {
118                 if event.tag == tag { Some((event.range, event.span.data())) } else { None }
119             })
120         };
121         Some(TagHistory::Tagged {
122             tag,
123             created: get_matching(&self.creations)?,
124             invalidated: get_matching(&self.invalidations),
125             protected,
126         })
127     }
128
129     /// Report a descriptive error when `new` could not be granted from `derived_from`.
130     pub fn grant_error<'tcx>(
131         &self,
132         derived_from: ProvenanceExtra,
133         new: Item,
134         alloc_id: AllocId,
135         alloc_range: AllocRange,
136         error_offset: Size,
137         stack: &Stack,
138     ) -> InterpError<'tcx> {
139         let action = format!(
140             "trying to reborrow from {derived_from:?} for {new_perm:?} permission at {alloc_id:?}[{offset:#x}]",
141             new_perm = new.perm(),
142             offset = error_offset.bytes(),
143         );
144         err_sb_ub(
145             format!("{}{}", action, error_cause(stack, derived_from)),
146             Some(operation_summary("a reborrow", alloc_id, alloc_range)),
147             derived_from.and_then(|derived_from| self.get_logs_relevant_to(derived_from, None)),
148         )
149     }
150
151     /// Report a descriptive error when `access` is not permitted based on `tag`.
152     pub fn access_error<'tcx>(
153         &self,
154         access: AccessKind,
155         tag: ProvenanceExtra,
156         alloc_id: AllocId,
157         alloc_range: AllocRange,
158         error_offset: Size,
159         stack: &Stack,
160     ) -> InterpError<'tcx> {
161         let action = format!(
162             "attempting a {access} using {tag:?} at {alloc_id:?}[{offset:#x}]",
163             offset = error_offset.bytes(),
164         );
165         err_sb_ub(
166             format!("{}{}", action, error_cause(stack, tag)),
167             Some(operation_summary("an access", alloc_id, alloc_range)),
168             tag.and_then(|tag| self.get_logs_relevant_to(tag, None)),
169         )
170     }
171 }
172
173 fn operation_summary(
174     operation: &'static str,
175     alloc_id: AllocId,
176     alloc_range: AllocRange,
177 ) -> String {
178     format!("this error occurs as part of {operation} at {alloc_id:?}{alloc_range:?}")
179 }
180
181 fn error_cause(stack: &Stack, prov_extra: ProvenanceExtra) -> &'static str {
182     if let ProvenanceExtra::Concrete(tag) = prov_extra {
183         if (0..stack.len())
184             .map(|i| stack.get(i).unwrap())
185             .any(|item| item.tag() == tag && item.perm() != Permission::Disabled)
186         {
187             ", but that tag only grants SharedReadOnly permission for this location"
188         } else {
189             ", but that tag does not exist in the borrow stack for this location"
190         }
191     } else {
192         ", but no exposed tags have suitable permission in the borrow stack for this location"
193     }
194 }