summaryrefslogtreecommitdiffstats
path: root/compiler/stable_mir/src/mir
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/stable_mir/src/mir')
-rw-r--r--compiler/stable_mir/src/mir/body.rs184
-rw-r--r--compiler/stable_mir/src/mir/mono.rs102
-rw-r--r--compiler/stable_mir/src/mir/visit.rs414
3 files changed, 656 insertions, 44 deletions
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index f93a1a3a9..069337836 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -1,11 +1,69 @@
-use crate::ty::{AdtDef, ClosureDef, Const, GeneratorDef, 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)]
pub struct Body {
pub blocks: Vec<BasicBlock>,
- pub locals: Vec<Ty>,
+
+ // Declarations of locals within the function.
+ //
+ // 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.
+ pub(super) locals: LocalDecls,
+
+ // The number of arguments this function takes.
+ pub(super) arg_count: usize,
+}
+
+impl Body {
+ /// Constructs a `Body`.
+ ///
+ /// A constructor is required to build a `Body` from outside the crate
+ /// because the `arg_count` and `locals` fields are private.
+ pub fn new(blocks: Vec<BasicBlock>, locals: LocalDecls, arg_count: usize) -> Self {
+ // If locals doesn't contain enough entries, it can lead to panics in
+ // `ret_local`, `arg_locals`, and `inner_locals`.
+ assert!(
+ locals.len() > arg_count,
+ "A Body must contain at least a local for the return value and each of the function's arguments"
+ );
+ Self { blocks, locals, arg_count }
+ }
+
+ /// Return local that holds this function's return value.
+ pub fn ret_local(&self) -> &LocalDecl {
+ &self.locals[RETURN_LOCAL]
+ }
+
+ /// Locals in `self` that correspond to this function's arguments.
+ pub fn arg_locals(&self) -> &[LocalDecl] {
+ &self.locals[1..][..self.arg_count]
+ }
+
+ /// Inner locals for this function. These are the locals that are
+ /// neither the return local nor the argument locals.
+ pub fn inner_locals(&self) -> &[LocalDecl] {
+ &self.locals[self.arg_count + 1..]
+ }
+
+ /// Convenience function to get all the locals in this function.
+ ///
+ /// Locals are typically accessed via the more specific methods `ret_local`,
+ /// `arg_locals`, and `inner_locals`.
+ pub fn locals(&self) -> &[LocalDecl] {
+ &self.locals
+ }
+}
+
+type LocalDecls = Vec<LocalDecl>;
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct LocalDecl {
+ pub ty: Ty,
+ pub span: Span,
}
#[derive(Clone, Debug)]
@@ -14,8 +72,14 @@ pub struct BasicBlock {
pub terminator: Terminator,
}
-#[derive(Clone, Debug)]
-pub enum Terminator {
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Terminator {
+ pub kind: TerminatorKind,
+ pub span: Span,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum TerminatorKind {
Goto {
target: usize,
},
@@ -47,7 +111,7 @@ pub enum Terminator {
target: usize,
unwind: UnwindAction,
},
- GeneratorDrop,
+ CoroutineDrop,
InlineAsm {
template: String,
operands: Vec<InlineAsmOperand>,
@@ -58,7 +122,7 @@ pub enum Terminator {
},
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InlineAsmOperand {
pub in_value: Option<Operand>,
pub out_place: Option<Place>,
@@ -67,7 +131,7 @@ pub struct InlineAsmOperand {
pub raw_rpr: String,
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub enum UnwindAction {
Continue,
Unreachable,
@@ -75,19 +139,19 @@ 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),
OverflowNeg(Operand),
DivisionByZero(Operand),
RemainderByZero(Operand),
- ResumedAfterReturn(GeneratorKind),
- ResumedAfterPanic(GeneratorKind),
+ ResumedAfterReturn(CoroutineKind),
+ ResumedAfterPanic(CoroutineKind),
MisalignedPointerDereference { required: Operand, found: Operand },
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BinOp {
Add,
AddUnchecked,
@@ -113,20 +177,21 @@ pub enum BinOp {
Offset,
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub enum UnOp {
Not,
Neg,
}
-#[derive(Clone, Debug)]
-pub enum GeneratorKind {
- Async(AsyncGeneratorKind),
- Gen,
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum CoroutineKind {
+ Async(CoroutineSource),
+ Coroutine,
+ Gen(CoroutineSource),
}
-#[derive(Clone, Debug)]
-pub enum AsyncGeneratorKind {
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum CoroutineSource {
Block,
Closure,
Fn,
@@ -139,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),
@@ -149,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,
@@ -157,7 +222,7 @@ pub enum RetagKind {
Default,
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Variance {
Covariant,
Invariant,
@@ -165,21 +230,27 @@ 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)]
-pub enum Statement {
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Statement {
+ pub kind: StatementKind,
+ pub span: Span,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum StatementKind {
Assign(Place, Rvalue),
FakeRead(FakeReadCause, Place),
SetDiscriminant { place: Place, variant_index: VariantIdx },
@@ -195,7 +266,7 @@ pub enum Statement {
Nop,
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Rvalue {
/// Creates a pointer with the indicated mutability to the place.
///
@@ -209,8 +280,8 @@ pub enum Rvalue {
/// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo`
/// has a destructor.
///
- /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Generator`. After
- /// generator lowering, `Generator` aggregate kinds are disallowed too.
+ /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After
+ /// coroutine lowering, `Coroutine` aggregate kinds are disallowed too.
Aggregate(AggregateKind, Vec<Operand>),
/// * `Offset` has the same semantics as `<*const T>::offset`, except that the second
@@ -307,29 +378,30 @@ pub enum Rvalue {
Use(Operand),
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub enum AggregateKind {
Array(Ty),
Tuple,
Adt(AdtDef, VariantIdx, GenericArgs, Option<UserTypeAnnotationIndex>, Option<FieldIdx>),
Closure(ClosureDef, GenericArgs),
- Generator(GeneratorDef, GenericArgs, Movability),
+ 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,
@@ -337,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.
@@ -344,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,
@@ -375,26 +449,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,
@@ -421,7 +495,7 @@ pub enum PointerCoercion {
Unsize,
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CastKind {
PointerExposeAddress,
PointerFromExposedAddress,
@@ -436,12 +510,34 @@ 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,
/// Returns the minimum alignment of a type.
AlignOf,
/// Returns the offset of a field.
- OffsetOf(Vec<FieldIdx>),
+ OffsetOf(Vec<(VariantIdx, FieldIdx)>),
+}
+
+impl Operand {
+ pub fn ty(&self, locals: &[LocalDecl]) -> Ty {
+ match self {
+ Operand::Copy(place) | Operand::Move(place) => place.ty(locals),
+ Operand::Constant(c) => c.ty(),
+ }
+ }
+}
+
+impl Constant {
+ pub fn ty(&self) -> Ty {
+ self.literal.ty()
+ }
+}
+
+impl Place {
+ pub fn ty(&self, locals: &[LocalDecl]) -> Ty {
+ let _start_ty = locals[self.local].ty;
+ todo!("Implement projection")
+ }
}
diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs
new file mode 100644
index 000000000..8f5333498
--- /dev/null
+++ b/compiler/stable_mir/src/mir/mono.rs
@@ -0,0 +1,102 @@
+use crate::mir::Body;
+use crate::ty::{FnDef, GenericArgs, IndexedVal, Ty};
+use crate::{with, CrateItem, DefId, Error, Opaque};
+use std::fmt::Debug;
+
+#[derive(Clone, Debug)]
+pub enum MonoItem {
+ Fn(Instance),
+ Static(StaticDef),
+ GlobalAsm(Opaque),
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct Instance {
+ /// The type of instance.
+ pub kind: InstanceKind,
+ /// An ID used to get the instance definition from the compiler.
+ /// Do not use this field directly.
+ pub def: InstanceDef,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum InstanceKind {
+ /// A user defined item.
+ Item,
+ /// A compiler intrinsic function.
+ Intrinsic,
+ /// A virtual function definition stored in a VTable.
+ Virtual,
+ /// A compiler generated shim.
+ Shim,
+}
+
+impl Instance {
+ /// Get the body of an Instance. The body will be eagerly monomorphized.
+ pub fn body(&self) -> Body {
+ with(|context| context.instance_body(self.def))
+ }
+
+ /// Get the instance type with generic substitutions applied and lifetimes erased.
+ pub fn ty(&self) -> Ty {
+ 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| {
+ context.resolve_instance(def, args).ok_or_else(|| {
+ crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`"))
+ })
+ })
+ }
+}
+
+/// Try to convert a crate item into an instance.
+/// The item cannot be generic in order to be converted into an instance.
+impl TryFrom<CrateItem> for Instance {
+ type Error = crate::Error;
+
+ fn try_from(item: CrateItem) -> Result<Self, Self::Error> {
+ with(|context| {
+ if !context.requires_monomorphization(item.0) {
+ Ok(context.mono_instance(item))
+ } else {
+ Err(Error::new("Item requires monomorphization".to_string()))
+ }
+ })
+ }
+}
+
+/// Try to convert an instance into a crate item.
+/// Only user defined instances can be converted.
+impl TryFrom<Instance> for CrateItem {
+ type Error = crate::Error;
+
+ fn try_from(value: Instance) -> Result<Self, Self::Error> {
+ if value.kind == InstanceKind::Item {
+ Ok(CrateItem(with(|context| context.instance_def_id(value.def))))
+ } else {
+ Err(Error::new(format!("Item kind `{:?}` cannot be converted", value.kind)))
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct InstanceDef(usize);
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct StaticDef(pub DefId);
+
+impl IndexedVal for InstanceDef {
+ fn to_val(index: usize) -> Self {
+ InstanceDef(index)
+ }
+ fn to_index(&self) -> usize {
+ self.0
+ }
+}
diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs
new file mode 100644
index 000000000..806dced71
--- /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
+ }
+}