
Fix needless_lifetimes in stable_mir Hi, This PR fixes the following clippy warning ``` warning: the following explicit lifetimes could be elided: 'a --> compiler/stable_mir/src/mir/visit.rs:490:6 | 490 | impl<'a> PlaceRef<'a> { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes = note: `#[warn(clippy::needless_lifetimes)]` on by default help: elide the lifetimes | 490 - impl<'a> PlaceRef<'a> { 490 + impl PlaceRef<'_> { | ``` Best regards, Michal
503 lines
18 KiB
Rust
503 lines
18 KiB
Rust
//! # 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::{GenericArgs, MirConst, Region, Ty, TyConst};
|
|
use crate::{Error, 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_projection_elem(
|
|
&mut self,
|
|
place_ref: PlaceRef<'_>,
|
|
elem: &ProjectionElem,
|
|
ptx: PlaceContext,
|
|
location: Location,
|
|
) {
|
|
let _ = place_ref;
|
|
self.super_projection_elem(elem, 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_const_operand(&mut self, constant: &ConstOperand, location: Location) {
|
|
self.super_const_operand(constant, location)
|
|
}
|
|
|
|
fn visit_mir_const(&mut self, constant: &MirConst, location: Location) {
|
|
self.super_mir_const(constant, location)
|
|
}
|
|
|
|
fn visit_ty_const(&mut self, constant: &TyConst, location: Location) {
|
|
let _ = location;
|
|
self.super_ty_const(constant)
|
|
}
|
|
|
|
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 visit_var_debug_info(&mut self, var_debug_info: &VarDebugInfo) {
|
|
self.super_var_debug_info(var_debug_info);
|
|
}
|
|
|
|
fn super_body(&mut self, body: &Body) {
|
|
let Body { blocks, locals: _, arg_count, var_debug_info, spread_arg: _, span } = 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.inner_locals().iter().enumerate() {
|
|
self.visit_local_decl(idx + local_start, arg)
|
|
}
|
|
|
|
for info in var_debug_info.iter() {
|
|
self.visit_var_debug_info(info);
|
|
}
|
|
|
|
self.visit_span(span)
|
|
}
|
|
|
|
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) | StatementKind::PlaceMention(place) => {
|
|
self.visit_place(place, PlaceContext::NON_MUTATING, location);
|
|
}
|
|
StatementKind::SetDiscriminant { place, .. }
|
|
| StatementKind::Deinit(place)
|
|
| StatementKind::Retag(_, place) => {
|
|
self.visit_place(place, PlaceContext::MUTATING, location);
|
|
}
|
|
StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
|
|
self.visit_local(local, PlaceContext::NON_USE, 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::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: _ } => {
|
|
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;
|
|
self.visit_local(&place.local, ptx, location);
|
|
|
|
for (idx, elem) in place.projection.iter().enumerate() {
|
|
let place_ref = PlaceRef { local: place.local, projection: &place.projection[..idx] };
|
|
self.visit_projection_elem(place_ref, elem, ptx, location);
|
|
}
|
|
}
|
|
|
|
fn super_projection_elem(
|
|
&mut self,
|
|
elem: &ProjectionElem,
|
|
ptx: PlaceContext,
|
|
location: Location,
|
|
) {
|
|
match elem {
|
|
ProjectionElem::Downcast(_idx) => {}
|
|
ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ }
|
|
| ProjectionElem::Deref
|
|
| ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {}
|
|
ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location),
|
|
ProjectionElem::Index(local) => self.visit_local(local, ptx, location),
|
|
ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => {
|
|
self.visit_ty(ty, location)
|
|
}
|
|
}
|
|
}
|
|
|
|
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_ty_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_const_operand(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_const_operand(&mut self, constant: &ConstOperand, location: Location) {
|
|
let ConstOperand { span, user_ty: _, const_ } = constant;
|
|
self.visit_span(span);
|
|
self.visit_mir_const(const_, location);
|
|
}
|
|
|
|
fn super_mir_const(&mut self, constant: &MirConst, location: Location) {
|
|
let MirConst { kind: _, ty, id: _ } = constant;
|
|
self.visit_ty(ty, location);
|
|
}
|
|
|
|
fn super_ty_const(&mut self, constant: &TyConst) {
|
|
let _ = constant;
|
|
}
|
|
|
|
fn super_region(&mut self, region: &Region) {
|
|
let _ = region;
|
|
}
|
|
|
|
fn super_args(&mut self, args: &GenericArgs) {
|
|
let _ = args;
|
|
}
|
|
|
|
fn super_var_debug_info(&mut self, var_debug_info: &VarDebugInfo) {
|
|
let VarDebugInfo { source_info, composite, value, name: _, argument_index: _ } =
|
|
var_debug_info;
|
|
self.visit_span(&source_info.span);
|
|
let location = Location(source_info.span);
|
|
if let Some(composite) = composite {
|
|
self.visit_ty(&composite.ty, location);
|
|
}
|
|
match value {
|
|
VarDebugInfoContents::Place(place) => {
|
|
self.visit_place(place, PlaceContext::NON_USE, location);
|
|
}
|
|
VarDebugInfoContents::Const(constant) => {
|
|
self.visit_mir_const(&constant.const_, location);
|
|
}
|
|
}
|
|
}
|
|
|
|
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, Debug)]
|
|
pub struct Location(Span);
|
|
|
|
impl Location {
|
|
pub fn span(&self) -> Span {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
/// Location of the statement at the given index for a given basic block. Assumes that `stmt_idx`
|
|
/// and `bb_idx` are valid for a given body.
|
|
pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) -> Location {
|
|
let bb = &body.blocks[*bb_idx];
|
|
let stmt = &bb.statements[stmt_idx];
|
|
Location(stmt.span)
|
|
}
|
|
|
|
/// Location of the terminator for a given basic block. Assumes that `bb_idx` is valid for a given
|
|
/// body.
|
|
pub fn terminator_location(body: &Body, bb_idx: &BasicBlockIdx) -> Location {
|
|
let bb = &body.blocks[*bb_idx];
|
|
let terminator = &bb.terminator;
|
|
Location(terminator.span)
|
|
}
|
|
|
|
/// Reference to a place used to represent a partial projection.
|
|
pub struct PlaceRef<'a> {
|
|
pub local: Local,
|
|
pub projection: &'a [ProjectionElem],
|
|
}
|
|
|
|
impl PlaceRef<'_> {
|
|
/// Get the type of this place.
|
|
pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> {
|
|
self.projection.iter().fold(Ok(locals[self.local].ty), |place_ty, elem| elem.ty(place_ty?))
|
|
}
|
|
}
|
|
|
|
/// Information about a place's usage.
|
|
#[derive(Copy, Clone, Debug, 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
|
|
}
|
|
}
|