1use std::collections::BTreeMap;
2
3use rustc_data_structures::either::Either;
4use rustc_data_structures::fx::FxHashSet;
5
6use crate::*;
7
8pub type VisitWith<'a> = dyn FnMut(Option<AllocId>, Option<BorTag>) + 'a;
9
10pub trait VisitProvenance {
11 fn visit_provenance(&self, visit: &mut VisitWith<'_>);
12}
13
14macro_rules! no_provenance {
16 ($($ty:ident)+) => {
17 $(
18 impl VisitProvenance for $ty {
19 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
20 }
21 )+
22 }
23}
24no_provenance!(i8 i16 i32 i64 isize u8 u16 u32 u64 usize bool ThreadId Deadline);
25
26impl VisitProvenance for &'static str {
27 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
28}
29
30impl<T: VisitProvenance> VisitProvenance for Option<T> {
31 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
32 if let Some(x) = self {
33 x.visit_provenance(visit);
34 }
35 }
36}
37
38impl<A, B> VisitProvenance for (A, B)
39where
40 A: VisitProvenance,
41 B: VisitProvenance,
42{
43 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
44 self.0.visit_provenance(visit);
45 self.1.visit_provenance(visit);
46 }
47}
48
49impl<T: VisitProvenance> VisitProvenance for Vec<T> {
50 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
51 self.iter().for_each(|el| el.visit_provenance(visit));
52 }
53}
54
55impl<K: VisitProvenance, V: VisitProvenance> VisitProvenance for BTreeMap<K, V> {
56 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
57 self.iter().for_each(|(key, value)| {
58 key.visit_provenance(visit);
59 value.visit_provenance(visit);
60 });
61 }
62}
63
64impl<T: VisitProvenance> VisitProvenance for std::cell::RefCell<T> {
65 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
66 self.borrow().visit_provenance(visit)
67 }
68}
69
70impl VisitProvenance for BorTag {
71 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
72 visit(None, Some(*self))
73 }
74}
75
76impl VisitProvenance for AllocId {
77 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
78 visit(Some(*self), None)
79 }
80}
81
82impl VisitProvenance for Provenance {
83 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
84 if let Provenance::Concrete { alloc_id, tag, .. } = self {
85 visit(Some(*alloc_id), Some(*tag));
86 }
87 }
88}
89
90impl VisitProvenance for StrictPointer {
91 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
92 self.provenance.visit_provenance(visit);
93 }
94}
95
96impl VisitProvenance for Pointer {
97 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
98 self.provenance.visit_provenance(visit);
99 }
100}
101
102impl VisitProvenance for Scalar {
103 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
104 match self {
105 Scalar::Ptr(ptr, _) => ptr.visit_provenance(visit),
106 Scalar::Int(_) => (),
107 }
108 }
109}
110
111impl VisitProvenance for IoError {
112 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
113 use crate::shims::io_error::IoError::*;
114 match self {
115 LibcError(_name) => (),
116 WindowsError(_name) => (),
117 HostError(_io_error) => (),
118 Raw(scalar) => scalar.visit_provenance(visit),
119 }
120 }
121}
122
123impl VisitProvenance for Immediate<Provenance> {
124 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
125 match self {
126 Immediate::Scalar(s) => {
127 s.visit_provenance(visit);
128 }
129 Immediate::ScalarPair(s1, s2) => {
130 s1.visit_provenance(visit);
131 s2.visit_provenance(visit);
132 }
133 Immediate::Uninit => {}
134 }
135 }
136}
137
138impl VisitProvenance for MemPlaceMeta<Provenance> {
139 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
140 match self {
141 MemPlaceMeta::Meta(m) => m.visit_provenance(visit),
142 MemPlaceMeta::None => {}
143 }
144 }
145}
146
147impl VisitProvenance for ImmTy<'_> {
148 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
149 (**self).visit_provenance(visit)
150 }
151}
152
153impl VisitProvenance for MPlaceTy<'_> {
154 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
155 self.ptr().visit_provenance(visit);
156 self.meta().visit_provenance(visit);
157 }
158}
159
160impl VisitProvenance for PlaceTy<'_> {
161 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
162 match self.as_mplace_or_local() {
163 Either::Left(mplace) => mplace.visit_provenance(visit),
164 Either::Right(_) => (),
165 }
166 }
167}
168
169impl VisitProvenance for OpTy<'_> {
170 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
171 match self.as_mplace_or_imm() {
172 Either::Left(mplace) => mplace.visit_provenance(visit),
173 Either::Right(imm) => imm.visit_provenance(visit),
174 }
175 }
176}
177
178impl VisitProvenance for Allocation<Provenance, AllocExtra<'_>, MiriAllocBytes> {
179 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
180 for prov in self.provenance().provenances() {
181 prov.visit_provenance(visit);
182 }
183
184 self.extra.visit_provenance(visit);
185 }
186}
187
188impl VisitProvenance for crate::MiriInterpCx<'_> {
189 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
190 self.memory.alloc_map().iter(|it| {
197 for (_id, (_kind, alloc)) in it {
198 alloc.visit_provenance(visit);
199 }
200 });
201 self.machine.visit_provenance(visit);
203 }
204}
205
206pub struct LiveAllocs<'a, 'tcx> {
207 collected: FxHashSet<AllocId>,
208 ecx: &'a MiriInterpCx<'tcx>,
209}
210
211impl LiveAllocs<'_, '_> {
212 pub fn is_live(&self, id: AllocId) -> bool {
213 self.collected.contains(&id) || self.ecx.is_alloc_live(id)
214 }
215}
216
217fn remove_unreachable_tags<'tcx>(ecx: &mut MiriInterpCx<'tcx>, tags: FxHashSet<BorTag>) {
218 if ecx.machine.borrow_tracker.is_some() {
220 ecx.memory.alloc_map().iter(|it| {
221 for (_id, (_kind, alloc)) in it {
222 alloc.extra.borrow_tracker.as_ref().unwrap().remove_unreachable_tags(&tags);
223 }
224 });
225 }
226}
227
228fn remove_unreachable_allocs<'tcx>(ecx: &mut MiriInterpCx<'tcx>, allocs: FxHashSet<AllocId>) {
229 let allocs = LiveAllocs { ecx, collected: allocs };
230 ecx.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
231 ecx.machine.symbolic_alignment.borrow_mut().retain(|id, _| allocs.is_live(*id));
232 ecx.machine.alloc_addresses.borrow_mut().remove_unreachable_allocs(&allocs);
233 if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
234 borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs);
235 }
236 ecx.remove_unreachable_allocs(&allocs.collected);
238}
239
240impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
241pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
242 fn run_provenance_gc(&mut self) {
243 let this = self.eval_context_mut();
244
245 let mut tags = FxHashSet::default();
247 let mut alloc_ids = FxHashSet::default();
248 this.visit_provenance(&mut |id, tag| {
249 if let Some(id) = id {
250 alloc_ids.insert(id);
251 }
252 if let Some(tag) = tag {
253 tags.insert(tag);
254 }
255 });
256
257 remove_unreachable_tags(this, tags);
259 remove_unreachable_allocs(this, alloc_ids);
260 }
261}