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