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 std::cell::RefCell<T> {
48 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
49 self.borrow().visit_provenance(visit)
50 }
51}
52
53impl VisitProvenance for BorTag {
54 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
55 visit(None, Some(*self))
56 }
57}
58
59impl VisitProvenance for AllocId {
60 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
61 visit(Some(*self), None)
62 }
63}
64
65impl VisitProvenance for Provenance {
66 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
67 if let Provenance::Concrete { alloc_id, tag, .. } = self {
68 visit(Some(*alloc_id), Some(*tag));
69 }
70 }
71}
72
73impl VisitProvenance for StrictPointer {
74 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
75 self.provenance.visit_provenance(visit);
76 }
77}
78
79impl VisitProvenance for Pointer {
80 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
81 self.provenance.visit_provenance(visit);
82 }
83}
84
85impl VisitProvenance for Scalar {
86 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
87 match self {
88 Scalar::Ptr(ptr, _) => ptr.visit_provenance(visit),
89 Scalar::Int(_) => (),
90 }
91 }
92}
93
94impl VisitProvenance for IoError {
95 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
96 use crate::shims::io_error::IoError::*;
97 match self {
98 LibcError(_name) => (),
99 WindowsError(_name) => (),
100 HostError(_io_error) => (),
101 Raw(scalar) => scalar.visit_provenance(visit),
102 }
103 }
104}
105
106impl VisitProvenance for Immediate<Provenance> {
107 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
108 match self {
109 Immediate::Scalar(s) => {
110 s.visit_provenance(visit);
111 }
112 Immediate::ScalarPair(s1, s2) => {
113 s1.visit_provenance(visit);
114 s2.visit_provenance(visit);
115 }
116 Immediate::Uninit => {}
117 }
118 }
119}
120
121impl VisitProvenance for MemPlaceMeta<Provenance> {
122 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
123 match self {
124 MemPlaceMeta::Meta(m) => m.visit_provenance(visit),
125 MemPlaceMeta::None => {}
126 }
127 }
128}
129
130impl VisitProvenance for ImmTy<'_> {
131 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
132 (**self).visit_provenance(visit)
133 }
134}
135
136impl VisitProvenance for MPlaceTy<'_> {
137 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
138 self.ptr().visit_provenance(visit);
139 self.meta().visit_provenance(visit);
140 }
141}
142
143impl VisitProvenance for PlaceTy<'_> {
144 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
145 match self.as_mplace_or_local() {
146 Either::Left(mplace) => mplace.visit_provenance(visit),
147 Either::Right(_) => (),
148 }
149 }
150}
151
152impl VisitProvenance for OpTy<'_> {
153 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
154 match self.as_mplace_or_imm() {
155 Either::Left(mplace) => mplace.visit_provenance(visit),
156 Either::Right(imm) => imm.visit_provenance(visit),
157 }
158 }
159}
160
161impl VisitProvenance for Allocation<Provenance, AllocExtra<'_>, MiriAllocBytes> {
162 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
163 for prov in self.provenance().provenances() {
164 prov.visit_provenance(visit);
165 }
166
167 self.extra.visit_provenance(visit);
168 }
169}
170
171impl VisitProvenance for crate::MiriInterpCx<'_> {
172 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
173 self.memory.alloc_map().iter(|it| {
180 for (_id, (_kind, alloc)) in it {
181 alloc.visit_provenance(visit);
182 }
183 });
184 self.machine.visit_provenance(visit);
186 }
187}
188
189pub struct LiveAllocs<'a, 'tcx> {
190 collected: FxHashSet<AllocId>,
191 ecx: &'a MiriInterpCx<'tcx>,
192}
193
194impl LiveAllocs<'_, '_> {
195 pub fn is_live(&self, id: AllocId) -> bool {
196 self.collected.contains(&id) || self.ecx.is_alloc_live(id)
197 }
198}
199
200fn remove_unreachable_tags<'tcx>(ecx: &mut MiriInterpCx<'tcx>, tags: FxHashSet<BorTag>) {
201 if ecx.machine.borrow_tracker.is_some() {
203 ecx.memory.alloc_map().iter(|it| {
204 for (_id, (_kind, alloc)) in it {
205 alloc.extra.borrow_tracker.as_ref().unwrap().remove_unreachable_tags(&tags);
206 }
207 });
208 }
209}
210
211fn remove_unreachable_allocs<'tcx>(ecx: &mut MiriInterpCx<'tcx>, allocs: FxHashSet<AllocId>) {
212 let allocs = LiveAllocs { ecx, collected: allocs };
213 ecx.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
214 ecx.machine.symbolic_alignment.borrow_mut().retain(|id, _| allocs.is_live(*id));
215 ecx.machine.alloc_addresses.borrow_mut().remove_unreachable_allocs(&allocs);
216 if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
217 borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs);
218 }
219 ecx.remove_unreachable_allocs(&allocs.collected);
221}
222
223impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
224pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
225 fn run_provenance_gc(&mut self) {
226 let this = self.eval_context_mut();
227
228 let mut tags = FxHashSet::default();
230 let mut alloc_ids = FxHashSet::default();
231 this.visit_provenance(&mut |id, tag| {
232 if let Some(id) = id {
233 alloc_ids.insert(id);
234 }
235 if let Some(tag) = tag {
236 tags.insert(tag);
237 }
238 });
239
240 remove_unreachable_tags(this, tags);
242 remove_unreachable_allocs(this, alloc_ids);
243 }
244}