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