about summary refs log tree commit diff
path: root/compiler/stable_mir/src/mir
diff options
context:
space:
mode:
authorCelina G. Val <celinval@amazon.com>2023-10-30 13:07:02 -0700
committerCelina G. Val <celinval@amazon.com>2023-10-30 13:11:14 -0700
commitaf7472ecbc40693680669c7667c81811a3eb949a (patch)
tree1a867e93e2d17ead0b59c44967bde1207e1cd6a0 /compiler/stable_mir/src/mir
parente6e931dda5fffbae0fd87c5b1af753cc95556880 (diff)
downloadrust-af7472ecbc40693680669c7667c81811a3eb949a.tar.gz
rust-af7472ecbc40693680669c7667c81811a3eb949a.zip
Add a stable MIR visitor
Add a few utility functions as well and extend most `mir` and `ty`
ADTs to implement `PartialEq` and `Eq`.
Diffstat (limited to 'compiler/stable_mir/src/mir')
-rw-r--r--compiler/stable_mir/src/mir/body.rs74
-rw-r--r--compiler/stable_mir/src/mir/mono.rs4
-rw-r--r--compiler/stable_mir/src/mir/visit.rs414
3 files changed, 456 insertions, 36 deletions
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 9f69e61d6fe..804f5e3d9d0 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -1,6 +1,6 @@
-use crate::ty::{AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region};
+use crate::ty::{AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, Ty};
 use crate::Opaque;
-use crate::{ty::Ty, Span};
+use crate::Span;
 
 /// The SMIR representation of a single function.
 #[derive(Clone, Debug)]
@@ -12,10 +12,10 @@ pub struct Body {
     // The first local is the return value pointer, followed by `arg_count`
     // locals for the function arguments, followed by any user-declared
     // variables and temporaries.
-    locals: LocalDecls,
+    pub(super) locals: LocalDecls,
 
     // The number of arguments this function takes.
-    arg_count: usize,
+    pub(super) arg_count: usize,
 }
 
 impl Body {
@@ -35,7 +35,7 @@ impl Body {
 
     /// Return local that holds this function's return value.
     pub fn ret_local(&self) -> &LocalDecl {
-        &self.locals[0]
+        &self.locals[RETURN_LOCAL]
     }
 
     /// Locals in `self` that correspond to this function's arguments.
@@ -60,7 +60,7 @@ impl Body {
 
 type LocalDecls = Vec<LocalDecl>;
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct LocalDecl {
     pub ty: Ty,
     pub span: Span,
@@ -72,13 +72,13 @@ pub struct BasicBlock {
     pub terminator: Terminator,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Terminator {
     pub kind: TerminatorKind,
     pub span: Span,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum TerminatorKind {
     Goto {
         target: usize,
@@ -122,7 +122,7 @@ pub enum TerminatorKind {
     },
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct InlineAsmOperand {
     pub in_value: Option<Operand>,
     pub out_place: Option<Place>,
@@ -131,7 +131,7 @@ pub struct InlineAsmOperand {
     pub raw_rpr: String,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum UnwindAction {
     Continue,
     Unreachable,
@@ -139,7 +139,7 @@ pub enum UnwindAction {
     Cleanup(usize),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum AssertMessage {
     BoundsCheck { len: Operand, index: Operand },
     Overflow(BinOp, Operand, Operand),
@@ -151,7 +151,7 @@ pub enum AssertMessage {
     MisalignedPointerDereference { required: Operand, found: Operand },
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum BinOp {
     Add,
     AddUnchecked,
@@ -177,20 +177,20 @@ pub enum BinOp {
     Offset,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum UnOp {
     Not,
     Neg,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum CoroutineKind {
     Async(CoroutineSource),
     Coroutine,
     Gen(CoroutineSource),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum CoroutineSource {
     Block,
     Closure,
@@ -204,7 +204,7 @@ pub(crate) type LocalDefId = Opaque;
 pub(crate) type Coverage = Opaque;
 
 /// The FakeReadCause describes the type of pattern why a FakeRead statement exists.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum FakeReadCause {
     ForMatchGuard,
     ForMatchedPlace(LocalDefId),
@@ -214,7 +214,7 @@ pub enum FakeReadCause {
 }
 
 /// Describes what kind of retag is to be performed
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
 pub enum RetagKind {
     FnEntry,
     TwoPhase,
@@ -222,7 +222,7 @@ pub enum RetagKind {
     Default,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
 pub enum Variance {
     Covariant,
     Invariant,
@@ -230,26 +230,26 @@ pub enum Variance {
     Bivariant,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct CopyNonOverlapping {
     pub src: Operand,
     pub dst: Operand,
     pub count: Operand,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum NonDivergingIntrinsic {
     Assume(Operand),
     CopyNonOverlapping(CopyNonOverlapping),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Statement {
     pub kind: StatementKind,
     pub span: Span,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum StatementKind {
     Assign(Place, Rvalue),
     FakeRead(FakeReadCause, Place),
@@ -266,7 +266,7 @@ pub enum StatementKind {
     Nop,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum Rvalue {
     /// Creates a pointer with the indicated mutability to the place.
     ///
@@ -378,7 +378,7 @@ pub enum Rvalue {
     Use(Operand),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum AggregateKind {
     Array(Ty),
     Tuple,
@@ -387,21 +387,21 @@ pub enum AggregateKind {
     Coroutine(CoroutineDef, GenericArgs, Movability),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum Operand {
     Copy(Place),
     Move(Place),
     Constant(Constant),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Place {
     pub local: Local,
     /// projection out of a place (access a field, deref a pointer, etc)
     pub projection: String,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct UserTypeProjection {
     pub base: UserTypeAnnotationIndex,
     pub projection: String,
@@ -409,6 +409,8 @@ pub struct UserTypeProjection {
 
 pub type Local = usize;
 
+pub const RETURN_LOCAL: Local = 0;
+
 type FieldIdx = usize;
 
 /// The source-order index of a variant in a type.
@@ -416,20 +418,20 @@ pub type VariantIdx = usize;
 
 type UserTypeAnnotationIndex = usize;
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Constant {
     pub span: Span,
     pub user_ty: Option<UserTypeAnnotationIndex>,
     pub literal: Const,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct SwitchTarget {
     pub value: u128,
     pub target: usize,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum BorrowKind {
     /// Data must be immutable and is aliasable.
     Shared,
@@ -446,26 +448,26 @@ pub enum BorrowKind {
     },
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum MutBorrowKind {
     Default,
     TwoPhaseBorrow,
     ClosureCapture,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum Mutability {
     Not,
     Mut,
 }
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub enum Safety {
     Unsafe,
     Normal,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum PointerCoercion {
     /// Go from a fn-item type to a fn-pointer type.
     ReifyFnPointer,
@@ -492,7 +494,7 @@ pub enum PointerCoercion {
     Unsize,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum CastKind {
     PointerExposeAddress,
     PointerFromExposedAddress,
@@ -507,7 +509,7 @@ pub enum CastKind {
     Transmute,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum NullOp {
     /// Returns the size of a value of that type.
     SizeOf,
diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs
index 997576fc7cb..8f533349848 100644
--- a/compiler/stable_mir/src/mir/mono.rs
+++ b/compiler/stable_mir/src/mir/mono.rs
@@ -42,6 +42,10 @@ impl Instance {
         with(|context| context.instance_ty(self.def))
     }
 
+    pub fn mangled_name(&self) -> String {
+        with(|context| context.instance_mangled_name(self.def))
+    }
+
     /// Resolve an instance starting from a function definition and generic arguments.
     pub fn resolve(def: FnDef, args: &GenericArgs) -> Result<Instance, crate::Error> {
         with(|context| {
diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs
new file mode 100644
index 00000000000..806dced71ff
--- /dev/null
+++ b/compiler/stable_mir/src/mir/visit.rs
@@ -0,0 +1,414 @@
+//! # The Stable MIR Visitor
+//!
+//! ## Overview
+//!
+//! We currently only support an immutable visitor.
+//! The structure of this visitor is similar to the ones internal to `rustc`,
+//! and it follows the following conventions:
+//!
+//! For every mir item, the trait has a `visit_<item>` and a `super_<item>` method.
+//! - `visit_<item>`, by default, calls `super_<item>`
+//! - `super_<item>`, by default, destructures the `<item>` and calls `visit_<sub_item>` for
+//!   all sub-items that compose the original item.
+//!
+//! In order to implement a visitor, override the `visit_*` methods for the types you are
+//! interested in analyzing, and invoke (within that method call)
+//! `self.super_*` to continue to the traverse.
+//! Avoid calling `super` methods in other circumstances.
+//!
+//! For the most part, we do not destructure things external to the
+//! MIR, e.g., types, spans, etc, but simply visit them and stop.
+//! This avoids duplication with other visitors like `TypeFoldable`.
+//!
+//! ## Updating
+//!
+//! The code is written in a very deliberate style intended to minimize
+//! the chance of things being overlooked.
+//!
+//! Use pattern matching to reference fields and ensure that all
+//! matches are exhaustive.
+//!
+//! For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS.
+//! That means you never write `..` to skip over fields, nor do you write `_`
+//! to skip over variants in a `match`.
+//!
+//! The only place that `_` is acceptable is to match a field (or
+//! variant argument) that does not require visiting.
+
+use crate::mir::*;
+use crate::ty::{Const, GenericArgs, Region, Ty};
+use crate::{Opaque, Span};
+
+pub trait MirVisitor {
+    fn visit_body(&mut self, body: &Body) {
+        self.super_body(body)
+    }
+
+    fn visit_basic_block(&mut self, bb: &BasicBlock) {
+        self.super_basic_block(bb)
+    }
+
+    fn visit_ret_decl(&mut self, local: Local, decl: &LocalDecl) {
+        self.super_ret_decl(local, decl)
+    }
+
+    fn visit_arg_decl(&mut self, local: Local, decl: &LocalDecl) {
+        self.super_arg_decl(local, decl)
+    }
+
+    fn visit_local_decl(&mut self, local: Local, decl: &LocalDecl) {
+        self.super_local_decl(local, decl)
+    }
+
+    fn visit_statement(&mut self, stmt: &Statement, location: Location) {
+        self.super_statement(stmt, location)
+    }
+
+    fn visit_terminator(&mut self, term: &Terminator, location: Location) {
+        self.super_terminator(term, location)
+    }
+
+    fn visit_span(&mut self, span: &Span) {
+        self.super_span(span)
+    }
+
+    fn visit_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) {
+        self.super_place(place, ptx, location)
+    }
+
+    fn visit_local(&mut self, local: &Local, ptx: PlaceContext, location: Location) {
+        let _ = (local, ptx, location);
+    }
+
+    fn visit_rvalue(&mut self, rvalue: &Rvalue, location: Location) {
+        self.super_rvalue(rvalue, location)
+    }
+
+    fn visit_operand(&mut self, operand: &Operand, location: Location) {
+        self.super_operand(operand, location)
+    }
+
+    fn visit_user_type_projection(&mut self, projection: &UserTypeProjection) {
+        self.super_user_type_projection(projection)
+    }
+
+    fn visit_ty(&mut self, ty: &Ty, location: Location) {
+        let _ = location;
+        self.super_ty(ty)
+    }
+
+    fn visit_constant(&mut self, constant: &Constant, location: Location) {
+        self.super_constant(constant, location)
+    }
+
+    fn visit_const(&mut self, constant: &Const, location: Location) {
+        self.super_const(constant, location)
+    }
+
+    fn visit_region(&mut self, region: &Region, location: Location) {
+        let _ = location;
+        self.super_region(region)
+    }
+
+    fn visit_args(&mut self, args: &GenericArgs, location: Location) {
+        let _ = location;
+        self.super_args(args)
+    }
+
+    fn visit_assert_msg(&mut self, msg: &AssertMessage, location: Location) {
+        self.super_assert_msg(msg, location)
+    }
+
+    fn super_body(&mut self, body: &Body) {
+        let Body { blocks, locals: _, arg_count } = body;
+
+        for bb in blocks {
+            self.visit_basic_block(bb);
+        }
+
+        self.visit_ret_decl(RETURN_LOCAL, body.ret_local());
+
+        for (idx, arg) in body.arg_locals().iter().enumerate() {
+            self.visit_arg_decl(idx + 1, arg)
+        }
+
+        let local_start = arg_count + 1;
+        for (idx, arg) in body.arg_locals().iter().enumerate() {
+            self.visit_local_decl(idx + local_start, arg)
+        }
+    }
+
+    fn super_basic_block(&mut self, bb: &BasicBlock) {
+        let BasicBlock { statements, terminator } = bb;
+        for stmt in statements {
+            self.visit_statement(stmt, Location(stmt.span));
+        }
+        self.visit_terminator(terminator, Location(terminator.span));
+    }
+
+    fn super_local_decl(&mut self, local: Local, decl: &LocalDecl) {
+        let _ = local;
+        let LocalDecl { ty, span } = decl;
+        self.visit_ty(ty, Location(*span));
+    }
+
+    fn super_ret_decl(&mut self, local: Local, decl: &LocalDecl) {
+        self.super_local_decl(local, decl)
+    }
+
+    fn super_arg_decl(&mut self, local: Local, decl: &LocalDecl) {
+        self.super_local_decl(local, decl)
+    }
+
+    fn super_statement(&mut self, stmt: &Statement, location: Location) {
+        let Statement { kind, span } = stmt;
+        self.visit_span(span);
+        match kind {
+            StatementKind::Assign(place, rvalue) => {
+                self.visit_place(place, PlaceContext::MUTATING, location);
+                self.visit_rvalue(rvalue, location);
+            }
+            StatementKind::FakeRead(_, place) => {
+                self.visit_place(place, PlaceContext::NON_MUTATING, location);
+            }
+            StatementKind::SetDiscriminant { place, .. } => {
+                self.visit_place(place, PlaceContext::MUTATING, location);
+            }
+            StatementKind::Deinit(place) => {
+                self.visit_place(place, PlaceContext::MUTATING, location);
+            }
+            StatementKind::StorageLive(local) => {
+                self.visit_local(local, PlaceContext::NON_USE, location);
+            }
+            StatementKind::StorageDead(local) => {
+                self.visit_local(local, PlaceContext::NON_USE, location);
+            }
+            StatementKind::Retag(_, place) => {
+                self.visit_place(place, PlaceContext::MUTATING, location);
+            }
+            StatementKind::PlaceMention(place) => {
+                self.visit_place(place, PlaceContext::NON_MUTATING, location);
+            }
+            StatementKind::AscribeUserType { place, projections, variance: _ } => {
+                self.visit_place(place, PlaceContext::NON_USE, location);
+                self.visit_user_type_projection(projections);
+            }
+            StatementKind::Coverage(coverage) => visit_opaque(coverage),
+            StatementKind::Intrinsic(intrisic) => match intrisic {
+                NonDivergingIntrinsic::Assume(operand) => {
+                    self.visit_operand(operand, location);
+                }
+                NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping {
+                    src,
+                    dst,
+                    count,
+                }) => {
+                    self.visit_operand(src, location);
+                    self.visit_operand(dst, location);
+                    self.visit_operand(count, location);
+                }
+            },
+            StatementKind::ConstEvalCounter => {}
+            StatementKind::Nop => {}
+        }
+    }
+
+    fn super_terminator(&mut self, term: &Terminator, location: Location) {
+        let Terminator { kind, span } = term;
+        self.visit_span(&span);
+        match kind {
+            TerminatorKind::Goto { .. }
+            | TerminatorKind::Resume
+            | TerminatorKind::Abort
+            | TerminatorKind::Unreachable
+            | TerminatorKind::CoroutineDrop => {}
+            TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
+                self.visit_operand(cond, location);
+                self.visit_assert_msg(msg, location);
+            }
+            TerminatorKind::Drop { place, target: _, unwind: _ } => {
+                self.visit_place(place, PlaceContext::MUTATING, location);
+            }
+            TerminatorKind::Call { func, args, destination, target: _, unwind: _ } => {
+                self.visit_operand(func, location);
+                for arg in args {
+                    self.visit_operand(arg, location);
+                }
+                self.visit_place(destination, PlaceContext::MUTATING, location);
+            }
+            TerminatorKind::InlineAsm { operands, .. } => {
+                for op in operands {
+                    let InlineAsmOperand { in_value, out_place, raw_rpr: _ } = op;
+                    if let Some(input) = in_value {
+                        self.visit_operand(input, location);
+                    }
+                    if let Some(output) = out_place {
+                        self.visit_place(output, PlaceContext::MUTATING, location);
+                    }
+                }
+            }
+            TerminatorKind::Return => {
+                let local = RETURN_LOCAL;
+                self.visit_local(&local, PlaceContext::NON_MUTATING, location);
+            }
+            TerminatorKind::SwitchInt { discr, targets: _, otherwise: _ } => {
+                self.visit_operand(discr, location);
+            }
+        }
+    }
+
+    fn super_span(&mut self, span: &Span) {
+        let _ = span;
+    }
+
+    fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) {
+        let _ = location;
+        let _ = ptx;
+        visit_opaque(&Opaque(place.projection.clone()));
+    }
+
+    fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) {
+        match rvalue {
+            Rvalue::AddressOf(mutability, place) => {
+                let pcx = PlaceContext { is_mut: *mutability == Mutability::Mut };
+                self.visit_place(place, pcx, location);
+            }
+            Rvalue::Aggregate(_, operands) => {
+                for op in operands {
+                    self.visit_operand(op, location);
+                }
+            }
+            Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
+                self.visit_operand(lhs, location);
+                self.visit_operand(rhs, location);
+            }
+            Rvalue::Cast(_, op, ty) => {
+                self.visit_operand(op, location);
+                self.visit_ty(ty, location);
+            }
+            Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) => {
+                self.visit_place(place, PlaceContext::NON_MUTATING, location);
+            }
+            Rvalue::Ref(region, kind, place) => {
+                self.visit_region(region, location);
+                let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) };
+                self.visit_place(place, pcx, location);
+            }
+            Rvalue::Repeat(op, constant) => {
+                self.visit_operand(op, location);
+                self.visit_const(constant, location);
+            }
+            Rvalue::ShallowInitBox(op, ty) => {
+                self.visit_ty(ty, location);
+                self.visit_operand(op, location)
+            }
+            Rvalue::ThreadLocalRef(_) => {}
+            Rvalue::NullaryOp(_, ty) => {
+                self.visit_ty(ty, location);
+            }
+            Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => {
+                self.visit_operand(op, location);
+            }
+        }
+    }
+
+    fn super_operand(&mut self, operand: &Operand, location: Location) {
+        match operand {
+            Operand::Copy(place) | Operand::Move(place) => {
+                self.visit_place(place, PlaceContext::NON_MUTATING, location)
+            }
+            Operand::Constant(constant) => {
+                self.visit_constant(constant, location);
+            }
+        }
+    }
+
+    fn super_user_type_projection(&mut self, projection: &UserTypeProjection) {
+        // This is a no-op on mir::Visitor.
+        let _ = projection;
+    }
+
+    fn super_ty(&mut self, ty: &Ty) {
+        let _ = ty;
+    }
+
+    fn super_constant(&mut self, constant: &Constant, location: Location) {
+        let Constant { span, user_ty: _, literal } = constant;
+        self.visit_span(span);
+        self.visit_const(literal, location);
+    }
+
+    fn super_const(&mut self, constant: &Const, location: Location) {
+        let Const { kind: _, ty, id: _ } = constant;
+        self.visit_ty(ty, location);
+    }
+
+    fn super_region(&mut self, region: &Region) {
+        let _ = region;
+    }
+
+    fn super_args(&mut self, args: &GenericArgs) {
+        let _ = args;
+    }
+
+    fn super_assert_msg(&mut self, msg: &AssertMessage, location: Location) {
+        match msg {
+            AssertMessage::BoundsCheck { len, index } => {
+                self.visit_operand(len, location);
+                self.visit_operand(index, location);
+            }
+            AssertMessage::Overflow(_, left, right) => {
+                self.visit_operand(left, location);
+                self.visit_operand(right, location);
+            }
+            AssertMessage::OverflowNeg(op)
+            | AssertMessage::DivisionByZero(op)
+            | AssertMessage::RemainderByZero(op) => {
+                self.visit_operand(op, location);
+            }
+            AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { //nothing to visit
+            }
+            AssertMessage::MisalignedPointerDereference { required, found } => {
+                self.visit_operand(required, location);
+                self.visit_operand(found, location);
+            }
+        }
+    }
+}
+
+/// This function is a no-op that gets used to ensure this visitor is kept up-to-date.
+///
+/// The idea is that whenever we replace an Opaque type by a real type, the compiler will fail
+/// when trying to invoke `visit_opaque`.
+///
+/// If you are here because your compilation is broken, replace the failing call to `visit_opaque()`
+/// by a `visit_<CONSTRUCT>` for your construct.
+fn visit_opaque(_: &Opaque) {}
+
+/// The location of a statement / terminator in the code and the CFG.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct Location(Span);
+
+impl Location {
+    pub fn span(&self) -> Span {
+        self.0
+    }
+}
+
+/// Information about a place's usage.
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct PlaceContext {
+    /// Whether the access is mutable or not. Keep this private so we can increment the type in a
+    /// backward compatible manner.
+    is_mut: bool,
+}
+
+impl PlaceContext {
+    const MUTATING: Self = PlaceContext { is_mut: true };
+    const NON_MUTATING: Self = PlaceContext { is_mut: false };
+    const NON_USE: Self = PlaceContext { is_mut: false };
+
+    pub fn is_mutating(&self) -> bool {
+        self.is_mut
+    }
+}