stable_mir/mir/
visit.rs

1//! # The Stable MIR Visitor
2//!
3//! ## Overview
4//!
5//! We currently only support an immutable visitor.
6//! The structure of this visitor is similar to the ones internal to `rustc`,
7//! and it follows the following conventions:
8//!
9//! For every mir item, the trait has a `visit_<item>` and a `super_<item>` method.
10//! - `visit_<item>`, by default, calls `super_<item>`
11//! - `super_<item>`, by default, destructures the `<item>` and calls `visit_<sub_item>` for
12//!   all sub-items that compose the original item.
13//!
14//! In order to implement a visitor, override the `visit_*` methods for the types you are
15//! interested in analyzing, and invoke (within that method call)
16//! `self.super_*` to continue to the traverse.
17//! Avoid calling `super` methods in other circumstances.
18//!
19//! For the most part, we do not destructure things external to the
20//! MIR, e.g., types, spans, etc, but simply visit them and stop.
21//! This avoids duplication with other visitors like `TypeFoldable`.
22//!
23//! ## Updating
24//!
25//! The code is written in a very deliberate style intended to minimize
26//! the chance of things being overlooked.
27//!
28//! Use pattern matching to reference fields and ensure that all
29//! matches are exhaustive.
30//!
31//! For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS.
32//! That means you never write `..` to skip over fields, nor do you write `_`
33//! to skip over variants in a `match`.
34//!
35//! The only place that `_` is acceptable is to match a field (or
36//! variant argument) that does not require visiting.
37
38use crate::mir::*;
39use crate::ty::{GenericArgs, MirConst, Region, Ty, TyConst};
40use crate::{Error, Opaque, Span};
41
42pub trait MirVisitor {
43    fn visit_body(&mut self, body: &Body) {
44        self.super_body(body)
45    }
46
47    fn visit_basic_block(&mut self, bb: &BasicBlock) {
48        self.super_basic_block(bb)
49    }
50
51    fn visit_ret_decl(&mut self, local: Local, decl: &LocalDecl) {
52        self.super_ret_decl(local, decl)
53    }
54
55    fn visit_arg_decl(&mut self, local: Local, decl: &LocalDecl) {
56        self.super_arg_decl(local, decl)
57    }
58
59    fn visit_local_decl(&mut self, local: Local, decl: &LocalDecl) {
60        self.super_local_decl(local, decl)
61    }
62
63    fn visit_statement(&mut self, stmt: &Statement, location: Location) {
64        self.super_statement(stmt, location)
65    }
66
67    fn visit_terminator(&mut self, term: &Terminator, location: Location) {
68        self.super_terminator(term, location)
69    }
70
71    fn visit_span(&mut self, span: &Span) {
72        self.super_span(span)
73    }
74
75    fn visit_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) {
76        self.super_place(place, ptx, location)
77    }
78
79    fn visit_projection_elem(
80        &mut self,
81        place_ref: PlaceRef<'_>,
82        elem: &ProjectionElem,
83        ptx: PlaceContext,
84        location: Location,
85    ) {
86        let _ = place_ref;
87        self.super_projection_elem(elem, ptx, location);
88    }
89
90    fn visit_local(&mut self, local: &Local, ptx: PlaceContext, location: Location) {
91        let _ = (local, ptx, location);
92    }
93
94    fn visit_rvalue(&mut self, rvalue: &Rvalue, location: Location) {
95        self.super_rvalue(rvalue, location)
96    }
97
98    fn visit_operand(&mut self, operand: &Operand, location: Location) {
99        self.super_operand(operand, location)
100    }
101
102    fn visit_user_type_projection(&mut self, projection: &UserTypeProjection) {
103        self.super_user_type_projection(projection)
104    }
105
106    fn visit_ty(&mut self, ty: &Ty, location: Location) {
107        let _ = location;
108        self.super_ty(ty)
109    }
110
111    fn visit_const_operand(&mut self, constant: &ConstOperand, location: Location) {
112        self.super_const_operand(constant, location)
113    }
114
115    fn visit_mir_const(&mut self, constant: &MirConst, location: Location) {
116        self.super_mir_const(constant, location)
117    }
118
119    fn visit_ty_const(&mut self, constant: &TyConst, location: Location) {
120        let _ = location;
121        self.super_ty_const(constant)
122    }
123
124    fn visit_region(&mut self, region: &Region, location: Location) {
125        let _ = location;
126        self.super_region(region)
127    }
128
129    fn visit_args(&mut self, args: &GenericArgs, location: Location) {
130        let _ = location;
131        self.super_args(args)
132    }
133
134    fn visit_assert_msg(&mut self, msg: &AssertMessage, location: Location) {
135        self.super_assert_msg(msg, location)
136    }
137
138    fn visit_var_debug_info(&mut self, var_debug_info: &VarDebugInfo) {
139        self.super_var_debug_info(var_debug_info);
140    }
141
142    fn super_body(&mut self, body: &Body) {
143        let Body { blocks, locals: _, arg_count, var_debug_info, spread_arg: _, span } = body;
144
145        for bb in blocks {
146            self.visit_basic_block(bb);
147        }
148
149        self.visit_ret_decl(RETURN_LOCAL, body.ret_local());
150
151        for (idx, arg) in body.arg_locals().iter().enumerate() {
152            self.visit_arg_decl(idx + 1, arg)
153        }
154
155        let local_start = arg_count + 1;
156        for (idx, arg) in body.inner_locals().iter().enumerate() {
157            self.visit_local_decl(idx + local_start, arg)
158        }
159
160        for info in var_debug_info.iter() {
161            self.visit_var_debug_info(info);
162        }
163
164        self.visit_span(span)
165    }
166
167    fn super_basic_block(&mut self, bb: &BasicBlock) {
168        let BasicBlock { statements, terminator } = bb;
169        for stmt in statements {
170            self.visit_statement(stmt, Location(stmt.span));
171        }
172        self.visit_terminator(terminator, Location(terminator.span));
173    }
174
175    fn super_local_decl(&mut self, local: Local, decl: &LocalDecl) {
176        let _ = local;
177        let LocalDecl { ty, span, .. } = decl;
178        self.visit_ty(ty, Location(*span));
179    }
180
181    fn super_ret_decl(&mut self, local: Local, decl: &LocalDecl) {
182        self.super_local_decl(local, decl)
183    }
184
185    fn super_arg_decl(&mut self, local: Local, decl: &LocalDecl) {
186        self.super_local_decl(local, decl)
187    }
188
189    fn super_statement(&mut self, stmt: &Statement, location: Location) {
190        let Statement { kind, span } = stmt;
191        self.visit_span(span);
192        match kind {
193            StatementKind::Assign(place, rvalue) => {
194                self.visit_place(place, PlaceContext::MUTATING, location);
195                self.visit_rvalue(rvalue, location);
196            }
197            StatementKind::FakeRead(_, place) | StatementKind::PlaceMention(place) => {
198                self.visit_place(place, PlaceContext::NON_MUTATING, location);
199            }
200            StatementKind::SetDiscriminant { place, .. }
201            | StatementKind::Deinit(place)
202            | StatementKind::Retag(_, place) => {
203                self.visit_place(place, PlaceContext::MUTATING, location);
204            }
205            StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
206                self.visit_local(local, PlaceContext::NON_USE, location);
207            }
208            StatementKind::AscribeUserType { place, projections, variance: _ } => {
209                self.visit_place(place, PlaceContext::NON_USE, location);
210                self.visit_user_type_projection(projections);
211            }
212            StatementKind::Coverage(coverage) => visit_opaque(coverage),
213            StatementKind::Intrinsic(intrisic) => match intrisic {
214                NonDivergingIntrinsic::Assume(operand) => {
215                    self.visit_operand(operand, location);
216                }
217                NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping {
218                    src,
219                    dst,
220                    count,
221                }) => {
222                    self.visit_operand(src, location);
223                    self.visit_operand(dst, location);
224                    self.visit_operand(count, location);
225                }
226            },
227            StatementKind::ConstEvalCounter | StatementKind::Nop => {}
228        }
229    }
230
231    fn super_terminator(&mut self, term: &Terminator, location: Location) {
232        let Terminator { kind, span } = term;
233        self.visit_span(span);
234        match kind {
235            TerminatorKind::Goto { .. }
236            | TerminatorKind::Resume
237            | TerminatorKind::Abort
238            | TerminatorKind::Unreachable => {}
239            TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
240                self.visit_operand(cond, location);
241                self.visit_assert_msg(msg, location);
242            }
243            TerminatorKind::Drop { place, target: _, unwind: _ } => {
244                self.visit_place(place, PlaceContext::MUTATING, location);
245            }
246            TerminatorKind::Call { func, args, destination, target: _, unwind: _ } => {
247                self.visit_operand(func, location);
248                for arg in args {
249                    self.visit_operand(arg, location);
250                }
251                self.visit_place(destination, PlaceContext::MUTATING, location);
252            }
253            TerminatorKind::InlineAsm { operands, .. } => {
254                for op in operands {
255                    let InlineAsmOperand { in_value, out_place, raw_rpr: _ } = op;
256                    if let Some(input) = in_value {
257                        self.visit_operand(input, location);
258                    }
259                    if let Some(output) = out_place {
260                        self.visit_place(output, PlaceContext::MUTATING, location);
261                    }
262                }
263            }
264            TerminatorKind::Return => {
265                let local = RETURN_LOCAL;
266                self.visit_local(&local, PlaceContext::NON_MUTATING, location);
267            }
268            TerminatorKind::SwitchInt { discr, targets: _ } => {
269                self.visit_operand(discr, location);
270            }
271        }
272    }
273
274    fn super_span(&mut self, span: &Span) {
275        let _ = span;
276    }
277
278    fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) {
279        let _ = location;
280        let _ = ptx;
281        self.visit_local(&place.local, ptx, location);
282
283        for (idx, elem) in place.projection.iter().enumerate() {
284            let place_ref = PlaceRef { local: place.local, projection: &place.projection[..idx] };
285            self.visit_projection_elem(place_ref, elem, ptx, location);
286        }
287    }
288
289    fn super_projection_elem(
290        &mut self,
291        elem: &ProjectionElem,
292        ptx: PlaceContext,
293        location: Location,
294    ) {
295        match elem {
296            ProjectionElem::Downcast(_idx) => {}
297            ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ }
298            | ProjectionElem::Deref
299            | ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {}
300            ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location),
301            ProjectionElem::Index(local) => self.visit_local(local, ptx, location),
302            ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => {
303                self.visit_ty(ty, location)
304            }
305        }
306    }
307
308    fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) {
309        match rvalue {
310            Rvalue::AddressOf(mutability, place) => {
311                let pcx = PlaceContext { is_mut: *mutability == RawPtrKind::Mut };
312                self.visit_place(place, pcx, location);
313            }
314            Rvalue::Aggregate(_, operands) => {
315                for op in operands {
316                    self.visit_operand(op, location);
317                }
318            }
319            Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
320                self.visit_operand(lhs, location);
321                self.visit_operand(rhs, location);
322            }
323            Rvalue::Cast(_, op, ty) => {
324                self.visit_operand(op, location);
325                self.visit_ty(ty, location);
326            }
327            Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) => {
328                self.visit_place(place, PlaceContext::NON_MUTATING, location);
329            }
330            Rvalue::Ref(region, kind, place) => {
331                self.visit_region(region, location);
332                let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) };
333                self.visit_place(place, pcx, location);
334            }
335            Rvalue::Repeat(op, constant) => {
336                self.visit_operand(op, location);
337                self.visit_ty_const(constant, location);
338            }
339            Rvalue::ShallowInitBox(op, ty) => {
340                self.visit_ty(ty, location);
341                self.visit_operand(op, location)
342            }
343            Rvalue::ThreadLocalRef(_) => {}
344            Rvalue::NullaryOp(_, ty) => {
345                self.visit_ty(ty, location);
346            }
347            Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => {
348                self.visit_operand(op, location);
349            }
350        }
351    }
352
353    fn super_operand(&mut self, operand: &Operand, location: Location) {
354        match operand {
355            Operand::Copy(place) | Operand::Move(place) => {
356                self.visit_place(place, PlaceContext::NON_MUTATING, location)
357            }
358            Operand::Constant(constant) => {
359                self.visit_const_operand(constant, location);
360            }
361        }
362    }
363
364    fn super_user_type_projection(&mut self, projection: &UserTypeProjection) {
365        // This is a no-op on mir::Visitor.
366        let _ = projection;
367    }
368
369    fn super_ty(&mut self, ty: &Ty) {
370        let _ = ty;
371    }
372
373    fn super_const_operand(&mut self, constant: &ConstOperand, location: Location) {
374        let ConstOperand { span, user_ty: _, const_ } = constant;
375        self.visit_span(span);
376        self.visit_mir_const(const_, location);
377    }
378
379    fn super_mir_const(&mut self, constant: &MirConst, location: Location) {
380        let MirConst { kind: _, ty, id: _ } = constant;
381        self.visit_ty(ty, location);
382    }
383
384    fn super_ty_const(&mut self, constant: &TyConst) {
385        let _ = constant;
386    }
387
388    fn super_region(&mut self, region: &Region) {
389        let _ = region;
390    }
391
392    fn super_args(&mut self, args: &GenericArgs) {
393        let _ = args;
394    }
395
396    fn super_var_debug_info(&mut self, var_debug_info: &VarDebugInfo) {
397        let VarDebugInfo { source_info, composite, value, name: _, argument_index: _ } =
398            var_debug_info;
399        self.visit_span(&source_info.span);
400        let location = Location(source_info.span);
401        if let Some(composite) = composite {
402            self.visit_ty(&composite.ty, location);
403        }
404        match value {
405            VarDebugInfoContents::Place(place) => {
406                self.visit_place(place, PlaceContext::NON_USE, location);
407            }
408            VarDebugInfoContents::Const(constant) => {
409                self.visit_mir_const(&constant.const_, location);
410            }
411        }
412    }
413
414    fn super_assert_msg(&mut self, msg: &AssertMessage, location: Location) {
415        match msg {
416            AssertMessage::BoundsCheck { len, index } => {
417                self.visit_operand(len, location);
418                self.visit_operand(index, location);
419            }
420            AssertMessage::Overflow(_, left, right) => {
421                self.visit_operand(left, location);
422                self.visit_operand(right, location);
423            }
424            AssertMessage::OverflowNeg(op)
425            | AssertMessage::DivisionByZero(op)
426            | AssertMessage::RemainderByZero(op) => {
427                self.visit_operand(op, location);
428            }
429            AssertMessage::ResumedAfterReturn(_)
430            | AssertMessage::ResumedAfterPanic(_)
431            | AssertMessage::NullPointerDereference => {
432                //nothing to visit
433            }
434            AssertMessage::MisalignedPointerDereference { required, found } => {
435                self.visit_operand(required, location);
436                self.visit_operand(found, location);
437            }
438        }
439    }
440}
441
442/// This function is a no-op that gets used to ensure this visitor is kept up-to-date.
443///
444/// The idea is that whenever we replace an Opaque type by a real type, the compiler will fail
445/// when trying to invoke `visit_opaque`.
446///
447/// If you are here because your compilation is broken, replace the failing call to `visit_opaque()`
448/// by a `visit_<CONSTRUCT>` for your construct.
449fn visit_opaque(_: &Opaque) {}
450
451/// The location of a statement / terminator in the code and the CFG.
452#[derive(Clone, Copy, PartialEq, Eq, Debug)]
453pub struct Location(Span);
454
455impl Location {
456    pub fn span(&self) -> Span {
457        self.0
458    }
459}
460
461/// Location of the statement at the given index for a given basic block. Assumes that `stmt_idx`
462/// and `bb_idx` are valid for a given body.
463pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) -> Location {
464    let bb = &body.blocks[*bb_idx];
465    let stmt = &bb.statements[stmt_idx];
466    Location(stmt.span)
467}
468
469/// Location of the terminator for a given basic block. Assumes that `bb_idx` is valid for a given
470/// body.
471pub fn terminator_location(body: &Body, bb_idx: &BasicBlockIdx) -> Location {
472    let bb = &body.blocks[*bb_idx];
473    let terminator = &bb.terminator;
474    Location(terminator.span)
475}
476
477/// Reference to a place used to represent a partial projection.
478pub struct PlaceRef<'a> {
479    pub local: Local,
480    pub projection: &'a [ProjectionElem],
481}
482
483impl PlaceRef<'_> {
484    /// Get the type of this place.
485    pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> {
486        self.projection.iter().fold(Ok(locals[self.local].ty), |place_ty, elem| elem.ty(place_ty?))
487    }
488}
489
490/// Information about a place's usage.
491#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
492pub struct PlaceContext {
493    /// Whether the access is mutable or not. Keep this private so we can increment the type in a
494    /// backward compatible manner.
495    is_mut: bool,
496}
497
498impl PlaceContext {
499    const MUTATING: Self = PlaceContext { is_mut: true };
500    const NON_MUTATING: Self = PlaceContext { is_mut: false };
501    const NON_USE: Self = PlaceContext { is_mut: false };
502
503    pub fn is_mutating(&self) -> bool {
504        self.is_mut
505    }
506}