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
42macro_rules! make_mir_visitor {
43    ($visitor_trait_name:ident, $($mutability:ident)?) => {
44        pub trait $visitor_trait_name {
45            fn visit_body(&mut self, body: &$($mutability)? Body) {
46                self.super_body(body)
47            }
48
49            fn visit_basic_block(&mut self, bb: &$($mutability)? BasicBlock) {
50                self.super_basic_block(bb)
51            }
52
53            fn visit_ret_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
54                self.super_ret_decl(local, decl)
55            }
56
57            fn visit_arg_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
58                self.super_arg_decl(local, decl)
59            }
60
61            fn visit_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
62                self.super_local_decl(local, decl)
63            }
64
65            fn visit_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) {
66                self.super_statement(stmt, location)
67            }
68
69            fn visit_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) {
70                self.super_terminator(term, location)
71            }
72
73            fn visit_span(&mut self, span: &$($mutability)? Span) {
74                self.super_span(span)
75            }
76
77            fn visit_place(&mut self, place: &$($mutability)? Place, ptx: PlaceContext, location: Location) {
78                self.super_place(place, ptx, location)
79            }
80
81            visit_place_fns!($($mutability)?);
82
83            fn visit_local(&mut self, local: &$($mutability)? Local, ptx: PlaceContext, location: Location) {
84                let _ = (local, ptx, location);
85            }
86
87            fn visit_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) {
88                self.super_rvalue(rvalue, location)
89            }
90
91            fn visit_operand(&mut self, operand: &$($mutability)? Operand, location: Location) {
92                self.super_operand(operand, location)
93            }
94
95            fn visit_user_type_projection(&mut self, projection: &$($mutability)? UserTypeProjection) {
96                self.super_user_type_projection(projection)
97            }
98
99            fn visit_ty(&mut self, ty: &$($mutability)? Ty, location: Location) {
100                let _ = location;
101                self.super_ty(ty)
102            }
103
104            fn visit_const_operand(&mut self, constant: &$($mutability)? ConstOperand, location: Location) {
105                self.super_const_operand(constant, location)
106            }
107
108            fn visit_mir_const(&mut self, constant: &$($mutability)? MirConst, location: Location) {
109                self.super_mir_const(constant, location)
110            }
111
112            fn visit_ty_const(&mut self, constant: &$($mutability)? TyConst, location: Location) {
113                let _ = location;
114                self.super_ty_const(constant)
115            }
116
117            fn visit_region(&mut self, region: &$($mutability)? Region, location: Location) {
118                let _ = location;
119                self.super_region(region)
120            }
121
122            fn visit_args(&mut self, args: &$($mutability)? GenericArgs, location: Location) {
123                let _ = location;
124                self.super_args(args)
125            }
126
127            fn visit_assert_msg(&mut self, msg: &$($mutability)? AssertMessage, location: Location) {
128                self.super_assert_msg(msg, location)
129            }
130
131            fn visit_var_debug_info(&mut self, var_debug_info: &$($mutability)? VarDebugInfo) {
132                self.super_var_debug_info(var_debug_info);
133            }
134
135            fn super_body(&mut self, body: &$($mutability)? Body) {
136                super_body!(self, body, $($mutability)?);
137            }
138
139            fn super_basic_block(&mut self, bb: &$($mutability)? BasicBlock) {
140                let BasicBlock { statements, terminator } = bb;
141                for stmt in statements {
142                    self.visit_statement(stmt, Location(stmt.span));
143                }
144                self.visit_terminator(terminator, Location(terminator.span));
145            }
146
147            fn super_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
148                let _ = local;
149                let LocalDecl { ty, span, .. } = decl;
150                self.visit_ty(ty, Location(*span));
151            }
152
153            fn super_ret_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
154                self.super_local_decl(local, decl)
155            }
156
157            fn super_arg_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
158                self.super_local_decl(local, decl)
159            }
160
161            fn super_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) {
162                let Statement { kind, span } = stmt;
163                self.visit_span(span);
164                match kind {
165                    StatementKind::Assign(place, rvalue) => {
166                        self.visit_place(place, PlaceContext::MUTATING, location);
167                        self.visit_rvalue(rvalue, location);
168                    }
169                    StatementKind::FakeRead(_, place) | StatementKind::PlaceMention(place) => {
170                        self.visit_place(place, PlaceContext::NON_MUTATING, location);
171                    }
172                    StatementKind::SetDiscriminant { place, .. }
173                    | StatementKind::Deinit(place)
174                    | StatementKind::Retag(_, place) => {
175                        self.visit_place(place, PlaceContext::MUTATING, location);
176                    }
177                    StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
178                        self.visit_local(local, PlaceContext::NON_USE, location);
179                    }
180                    StatementKind::AscribeUserType { place, projections, variance: _ } => {
181                        self.visit_place(place, PlaceContext::NON_USE, location);
182                        self.visit_user_type_projection(projections);
183                    }
184                    StatementKind::Coverage(coverage) => visit_opaque(coverage),
185                    StatementKind::Intrinsic(intrisic) => match intrisic {
186                        NonDivergingIntrinsic::Assume(operand) => {
187                            self.visit_operand(operand, location);
188                        }
189                        NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping {
190                            src,
191                            dst,
192                            count,
193                        }) => {
194                            self.visit_operand(src, location);
195                            self.visit_operand(dst, location);
196                            self.visit_operand(count, location);
197                        }
198                    },
199                    StatementKind::ConstEvalCounter | StatementKind::Nop => {}
200                }
201            }
202
203            fn super_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) {
204                let Terminator { kind, span } = term;
205                self.visit_span(span);
206                match kind {
207                    TerminatorKind::Goto { .. }
208                    | TerminatorKind::Resume
209                    | TerminatorKind::Abort
210                    | TerminatorKind::Unreachable => {}
211                    TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
212                        self.visit_operand(cond, location);
213                        self.visit_assert_msg(msg, location);
214                    }
215                    TerminatorKind::Drop { place, target: _, unwind: _ } => {
216                        self.visit_place(place, PlaceContext::MUTATING, location);
217                    }
218                    TerminatorKind::Call { func, args, destination, target: _, unwind: _ } => {
219                        self.visit_operand(func, location);
220                        for arg in args {
221                            self.visit_operand(arg, location);
222                        }
223                        self.visit_place(destination, PlaceContext::MUTATING, location);
224                    }
225                    TerminatorKind::InlineAsm { operands, .. } => {
226                        for op in operands {
227                            let InlineAsmOperand { in_value, out_place, raw_rpr: _ } = op;
228                            if let Some(input) = in_value {
229                                self.visit_operand(input, location);
230                            }
231                            if let Some(output) = out_place {
232                                self.visit_place(output, PlaceContext::MUTATING, location);
233                            }
234                        }
235                    }
236                    TerminatorKind::Return => {
237                        let $($mutability)? local = RETURN_LOCAL;
238                        self.visit_local(&$($mutability)? local, PlaceContext::NON_MUTATING, location);
239                    }
240                    TerminatorKind::SwitchInt { discr, targets: _ } => {
241                        self.visit_operand(discr, location);
242                    }
243                }
244            }
245
246            fn super_span(&mut self, span: &$($mutability)? Span) {
247                let _ = span;
248            }
249
250            fn super_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) {
251                match rvalue {
252                    Rvalue::AddressOf(mutability, place) => {
253                        let pcx = PlaceContext { is_mut: *mutability == RawPtrKind::Mut };
254                        self.visit_place(place, pcx, location);
255                    }
256                    Rvalue::Aggregate(_, operands) => {
257                        for op in operands {
258                            self.visit_operand(op, location);
259                        }
260                    }
261                    Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
262                        self.visit_operand(lhs, location);
263                        self.visit_operand(rhs, location);
264                    }
265                    Rvalue::Cast(_, op, ty) => {
266                        self.visit_operand(op, location);
267                        self.visit_ty(ty, location);
268                    }
269                    Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) => {
270                        self.visit_place(place, PlaceContext::NON_MUTATING, location);
271                    }
272                    Rvalue::Ref(region, kind, place) => {
273                        self.visit_region(region, location);
274                        let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) };
275                        self.visit_place(place, pcx, location);
276                    }
277                    Rvalue::Repeat(op, constant) => {
278                        self.visit_operand(op, location);
279                        self.visit_ty_const(constant, location);
280                    }
281                    Rvalue::ShallowInitBox(op, ty) => {
282                        self.visit_ty(ty, location);
283                        self.visit_operand(op, location)
284                    }
285                    Rvalue::ThreadLocalRef(_) => {}
286                    Rvalue::NullaryOp(_, ty) => {
287                        self.visit_ty(ty, location);
288                    }
289                    Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => {
290                        self.visit_operand(op, location);
291                    }
292                }
293            }
294
295            fn super_operand(&mut self, operand: &$($mutability)? Operand, location: Location) {
296                match operand {
297                    Operand::Copy(place) | Operand::Move(place) => {
298                        self.visit_place(place, PlaceContext::NON_MUTATING, location)
299                    }
300                    Operand::Constant(constant) => {
301                        self.visit_const_operand(constant, location);
302                    }
303                }
304            }
305
306            fn super_user_type_projection(&mut self, projection: &$($mutability)? UserTypeProjection) {
307                // This is a no-op on mir::Visitor.
308                let _ = projection;
309            }
310
311            fn super_ty(&mut self, ty: &$($mutability)? Ty) {
312                let _ = ty;
313            }
314
315            fn super_const_operand(&mut self, constant: &$($mutability)? ConstOperand, location: Location) {
316                let ConstOperand { span, user_ty: _, const_ } = constant;
317                self.visit_span(span);
318                self.visit_mir_const(const_, location);
319            }
320
321            fn super_mir_const(&mut self, constant: &$($mutability)? MirConst, location: Location) {
322                let MirConst { kind: _, ty, id: _ } = constant;
323                self.visit_ty(ty, location);
324            }
325
326            fn super_ty_const(&mut self, constant: &$($mutability)? TyConst) {
327                let _ = constant;
328            }
329
330            fn super_region(&mut self, region: &$($mutability)? Region) {
331                let _ = region;
332            }
333
334            fn super_args(&mut self, args: &$($mutability)? GenericArgs) {
335                let _ = args;
336            }
337
338            fn super_var_debug_info(&mut self, var_debug_info: &$($mutability)? VarDebugInfo) {
339                let VarDebugInfo { source_info, composite, value, name: _, argument_index: _ } =
340                    var_debug_info;
341                self.visit_span(&$($mutability)? source_info.span);
342                let location = Location(source_info.span);
343                if let Some(composite) = composite {
344                    self.visit_ty(&$($mutability)? composite.ty, location);
345                }
346                match value {
347                    VarDebugInfoContents::Place(place) => {
348                        self.visit_place(place, PlaceContext::NON_USE, location);
349                    }
350                    VarDebugInfoContents::Const(constant) => {
351                        self.visit_mir_const(&$($mutability)? constant.const_, location);
352                    }
353                }
354            }
355
356            fn super_assert_msg(&mut self, msg: &$($mutability)? AssertMessage, location: Location) {
357                match msg {
358                    AssertMessage::BoundsCheck { len, index } => {
359                        self.visit_operand(len, location);
360                        self.visit_operand(index, location);
361                    }
362                    AssertMessage::Overflow(_, left, right) => {
363                        self.visit_operand(left, location);
364                        self.visit_operand(right, location);
365                    }
366                    AssertMessage::OverflowNeg(op)
367                    | AssertMessage::DivisionByZero(op)
368                    | AssertMessage::RemainderByZero(op) => {
369                        self.visit_operand(op, location);
370                    }
371                    AssertMessage::ResumedAfterReturn(_)
372                    | AssertMessage::ResumedAfterPanic(_)
373                    | AssertMessage::NullPointerDereference => {
374                        //nothing to visit
375                    }
376                    AssertMessage::MisalignedPointerDereference { required, found } => {
377                        self.visit_operand(required, location);
378                        self.visit_operand(found, location);
379                    }
380                }
381            }
382        }
383    };
384}
385
386macro_rules! super_body {
387    ($self:ident, $body:ident, mut) => {
388        for bb in $body.blocks.iter_mut() {
389            $self.visit_basic_block(bb);
390        }
391
392        $self.visit_ret_decl(RETURN_LOCAL, $body.ret_local_mut());
393
394        for (idx, arg) in $body.arg_locals_mut().iter_mut().enumerate() {
395            $self.visit_arg_decl(idx + 1, arg)
396        }
397
398        let local_start = $body.arg_count + 1;
399        for (idx, arg) in $body.inner_locals_mut().iter_mut().enumerate() {
400            $self.visit_local_decl(idx + local_start, arg)
401        }
402
403        for info in $body.var_debug_info.iter_mut() {
404            $self.visit_var_debug_info(info);
405        }
406
407        $self.visit_span(&mut $body.span)
408    };
409
410    ($self:ident, $body:ident, ) => {
411        let Body { blocks, locals: _, arg_count, var_debug_info, spread_arg: _, span } = $body;
412
413        for bb in blocks {
414            $self.visit_basic_block(bb);
415        }
416
417        $self.visit_ret_decl(RETURN_LOCAL, $body.ret_local());
418
419        for (idx, arg) in $body.arg_locals().iter().enumerate() {
420            $self.visit_arg_decl(idx + 1, arg)
421        }
422
423        let local_start = arg_count + 1;
424        for (idx, arg) in $body.inner_locals().iter().enumerate() {
425            $self.visit_local_decl(idx + local_start, arg)
426        }
427
428        for info in var_debug_info.iter() {
429            $self.visit_var_debug_info(info);
430        }
431
432        $self.visit_span(span)
433    };
434}
435
436macro_rules! visit_place_fns {
437    (mut) => {
438        fn super_place(&mut self, place: &mut Place, ptx: PlaceContext, location: Location) {
439            self.visit_local(&mut place.local, ptx, location);
440
441            for elem in place.projection.iter_mut() {
442                self.visit_projection_elem(elem, ptx, location);
443            }
444        }
445
446        // We don't have to replicate the `process_projection()` like we did in
447        // `rustc_middle::mir::visit.rs` here because the `projection` field in `Place`
448        // of Stable-MIR is not an immutable borrow, unlike in `Place` of MIR.
449        fn visit_projection_elem(
450            &mut self,
451            elem: &mut ProjectionElem,
452            ptx: PlaceContext,
453            location: Location,
454        ) {
455            self.super_projection_elem(elem, ptx, location)
456        }
457
458        fn super_projection_elem(
459            &mut self,
460            elem: &mut ProjectionElem,
461            ptx: PlaceContext,
462            location: Location,
463        ) {
464            match elem {
465                ProjectionElem::Deref => {}
466                ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location),
467                ProjectionElem::Index(local) => self.visit_local(local, ptx, location),
468                ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {}
469                ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {}
470                ProjectionElem::Downcast(_idx) => {}
471                ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location),
472                ProjectionElem::Subtype(ty) => self.visit_ty(ty, location),
473            }
474        }
475    };
476
477    () => {
478        fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) {
479            self.visit_local(&place.local, ptx, location);
480
481            for (idx, elem) in place.projection.iter().enumerate() {
482                let place_ref =
483                    PlaceRef { local: place.local, projection: &place.projection[..idx] };
484                self.visit_projection_elem(place_ref, elem, ptx, location);
485            }
486        }
487
488        fn visit_projection_elem<'a>(
489            &mut self,
490            place_ref: PlaceRef<'a>,
491            elem: &ProjectionElem,
492            ptx: PlaceContext,
493            location: Location,
494        ) {
495            let _ = place_ref;
496            self.super_projection_elem(elem, ptx, location);
497        }
498
499        fn super_projection_elem(
500            &mut self,
501            elem: &ProjectionElem,
502            ptx: PlaceContext,
503            location: Location,
504        ) {
505            match elem {
506                ProjectionElem::Deref => {}
507                ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location),
508                ProjectionElem::Index(local) => self.visit_local(local, ptx, location),
509                ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {}
510                ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {}
511                ProjectionElem::Downcast(_idx) => {}
512                ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location),
513                ProjectionElem::Subtype(ty) => self.visit_ty(ty, location),
514            }
515        }
516    };
517}
518
519make_mir_visitor!(MirVisitor,);
520make_mir_visitor!(MutMirVisitor, mut);
521
522/// This function is a no-op that gets used to ensure this visitor is kept up-to-date.
523///
524/// The idea is that whenever we replace an Opaque type by a real type, the compiler will fail
525/// when trying to invoke `visit_opaque`.
526///
527/// If you are here because your compilation is broken, replace the failing call to `visit_opaque()`
528/// by a `visit_<CONSTRUCT>` for your construct.
529fn visit_opaque(_: &Opaque) {}
530
531/// The location of a statement / terminator in the code and the CFG.
532#[derive(Clone, Copy, PartialEq, Eq, Debug)]
533pub struct Location(Span);
534
535impl Location {
536    pub fn span(&self) -> Span {
537        self.0
538    }
539}
540
541/// Location of the statement at the given index for a given basic block. Assumes that `stmt_idx`
542/// and `bb_idx` are valid for a given body.
543pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) -> Location {
544    let bb = &body.blocks[*bb_idx];
545    let stmt = &bb.statements[stmt_idx];
546    Location(stmt.span)
547}
548
549/// Location of the terminator for a given basic block. Assumes that `bb_idx` is valid for a given
550/// body.
551pub fn terminator_location(body: &Body, bb_idx: &BasicBlockIdx) -> Location {
552    let bb = &body.blocks[*bb_idx];
553    let terminator = &bb.terminator;
554    Location(terminator.span)
555}
556
557/// Reference to a place used to represent a partial projection.
558pub struct PlaceRef<'a> {
559    pub local: Local,
560    pub projection: &'a [ProjectionElem],
561}
562
563impl PlaceRef<'_> {
564    /// Get the type of this place.
565    pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> {
566        self.projection.iter().fold(Ok(locals[self.local].ty), |place_ty, elem| elem.ty(place_ty?))
567    }
568}
569
570/// Information about a place's usage.
571#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
572pub struct PlaceContext {
573    /// Whether the access is mutable or not. Keep this private so we can increment the type in a
574    /// backward compatible manner.
575    is_mut: bool,
576}
577
578impl PlaceContext {
579    const MUTATING: Self = PlaceContext { is_mut: true };
580    const NON_MUTATING: Self = PlaceContext { is_mut: false };
581    const NON_USE: Self = PlaceContext { is_mut: false };
582
583    pub fn is_mutating(&self) -> bool {
584        self.is_mut
585    }
586}