From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_middle/src/mir/visit.rs | 1330 ++++++++++++++++++++++++++++++++ 1 file changed, 1330 insertions(+) create mode 100644 compiler/rustc_middle/src/mir/visit.rs (limited to 'compiler/rustc_middle/src/mir/visit.rs') diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs new file mode 100644 index 000000000..891608764 --- /dev/null +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -0,0 +1,1330 @@ +//! # The MIR Visitor +//! +//! ## Overview +//! +//! There are two visitors, one for immutable and one for mutable references, +//! but both are generated by the following macro. The code is written according +//! to the following conventions: +//! +//! - introduce a `visit_foo` and a `super_foo` method for every MIR type +//! - `visit_foo`, by default, calls `super_foo` +//! - `super_foo`, by default, destructures the `foo` and calls `visit_foo` +//! +//! This allows you as a user to override `visit_foo` for types are +//! interested in, and invoke (within that method) call +//! `self.super_foo` to get the default behavior. Just as in an OO +//! language, you should never call `super` methods ordinarily except +//! in that circumstance. +//! +//! 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. You'll notice that we always +//! use pattern matching to reference fields and we ensure that all +//! matches are exhaustive. +//! +//! For example, the `super_basic_block_data` method begins like this: +//! +//! ```ignore (pseudo-rust) +//! fn super_basic_block_data( +//! &mut self, +//! block: BasicBlock, +//! data: & $($mutability)? BasicBlockData<'tcx> +//! ) { +//! let BasicBlockData { +//! statements, +//! terminator, +//! is_cleanup: _ +//! } = *data; +//! +//! for statement in statements { +//! self.visit_statement(block, statement); +//! } +//! +//! ... +//! } +//! ``` +//! +//! Here we used `let BasicBlockData { } = *data` deliberately, +//! rather than writing `data.statements` in the body. This is because if one +//! adds a new field to `BasicBlockData`, one will be forced to revise this code, +//! and hence one will (hopefully) invoke the correct visit methods (if any). +//! +//! 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, as in +//! `is_cleanup` above. + +use crate::mir::*; +use crate::ty::subst::SubstsRef; +use crate::ty::{CanonicalUserTypeAnnotation, Ty}; +use rustc_span::Span; + +macro_rules! make_mir_visitor { + ($visitor_trait_name:ident, $($mutability:ident)?) => { + pub trait $visitor_trait_name<'tcx> { + // Override these, and call `self.super_xxx` to revert back to the + // default behavior. + + fn visit_body( + &mut self, + body: &$($mutability)? Body<'tcx>, + ) { + self.super_body(body); + } + + fn visit_basic_block_data( + &mut self, + block: BasicBlock, + data: & $($mutability)? BasicBlockData<'tcx>, + ) { + self.super_basic_block_data(block, data); + } + + fn visit_source_scope_data( + &mut self, + scope_data: & $($mutability)? SourceScopeData<'tcx>, + ) { + self.super_source_scope_data(scope_data); + } + + fn visit_statement( + &mut self, + statement: & $($mutability)? Statement<'tcx>, + location: Location, + ) { + self.super_statement(statement, location); + } + + fn visit_assign( + &mut self, + place: & $($mutability)? Place<'tcx>, + rvalue: & $($mutability)? Rvalue<'tcx>, + location: Location, + ) { + self.super_assign(place, rvalue, location); + } + + fn visit_terminator( + &mut self, + terminator: & $($mutability)? Terminator<'tcx>, + location: Location, + ) { + self.super_terminator(terminator, location); + } + + fn visit_assert_message( + &mut self, + msg: & $($mutability)? AssertMessage<'tcx>, + location: Location, + ) { + self.super_assert_message(msg, location); + } + + fn visit_rvalue( + &mut self, + rvalue: & $($mutability)? Rvalue<'tcx>, + location: Location, + ) { + self.super_rvalue(rvalue, location); + } + + fn visit_operand( + &mut self, + operand: & $($mutability)? Operand<'tcx>, + location: Location, + ) { + self.super_operand(operand, location); + } + + fn visit_ascribe_user_ty( + &mut self, + place: & $($mutability)? Place<'tcx>, + variance: $(& $mutability)? ty::Variance, + user_ty: & $($mutability)? UserTypeProjection, + location: Location, + ) { + self.super_ascribe_user_ty(place, variance, user_ty, location); + } + + fn visit_coverage( + &mut self, + coverage: & $($mutability)? Coverage, + location: Location, + ) { + self.super_coverage(coverage, location); + } + + fn visit_retag( + &mut self, + kind: $(& $mutability)? RetagKind, + place: & $($mutability)? Place<'tcx>, + location: Location, + ) { + self.super_retag(kind, place, location); + } + + fn visit_place( + &mut self, + place: & $($mutability)? Place<'tcx>, + context: PlaceContext, + location: Location, + ) { + self.super_place(place, context, location); + } + + visit_place_fns!($($mutability)?); + + fn visit_constant( + &mut self, + constant: & $($mutability)? Constant<'tcx>, + location: Location, + ) { + self.super_constant(constant, location); + } + + fn visit_span( + &mut self, + span: $(& $mutability)? Span, + ) { + self.super_span(span); + } + + fn visit_source_info( + &mut self, + source_info: & $($mutability)? SourceInfo, + ) { + self.super_source_info(source_info); + } + + fn visit_ty( + &mut self, + ty: $(& $mutability)? Ty<'tcx>, + _: TyContext, + ) { + self.super_ty(ty); + } + + fn visit_user_type_projection( + &mut self, + ty: & $($mutability)? UserTypeProjection, + ) { + self.super_user_type_projection(ty); + } + + fn visit_user_type_annotation( + &mut self, + index: UserTypeAnnotationIndex, + ty: & $($mutability)? CanonicalUserTypeAnnotation<'tcx>, + ) { + self.super_user_type_annotation(index, ty); + } + + fn visit_region( + &mut self, + region: $(& $mutability)? ty::Region<'tcx>, + _: Location, + ) { + self.super_region(region); + } + + fn visit_const( + &mut self, + constant: $(& $mutability)? ty::Const<'tcx>, + _: Location, + ) { + self.super_const(constant); + } + + fn visit_substs( + &mut self, + substs: & $($mutability)? SubstsRef<'tcx>, + _: Location, + ) { + self.super_substs(substs); + } + + fn visit_local_decl( + &mut self, + local: Local, + local_decl: & $($mutability)? LocalDecl<'tcx>, + ) { + self.super_local_decl(local, local_decl); + } + + fn visit_var_debug_info( + &mut self, + var_debug_info: & $($mutability)* VarDebugInfo<'tcx>, + ) { + self.super_var_debug_info(var_debug_info); + } + + fn visit_local( + &mut self, + _local: $(& $mutability)? Local, + _context: PlaceContext, + _location: Location, + ) {} + + fn visit_source_scope( + &mut self, + scope: $(& $mutability)? SourceScope, + ) { + self.super_source_scope(scope); + } + + // The `super_xxx` methods comprise the default behavior and are + // not meant to be overridden. + + fn super_body( + &mut self, + body: &$($mutability)? Body<'tcx>, + ) { + let span = body.span; + if let Some(gen) = &$($mutability)? body.generator { + if let Some(yield_ty) = $(& $mutability)? gen.yield_ty { + self.visit_ty( + yield_ty, + TyContext::YieldTy(SourceInfo::outermost(span)) + ); + } + } + + // for best performance, we want to use an iterator rather + // than a for-loop, to avoid calling `body::Body::invalidate` for + // each basic block. + #[allow(unused_macro_rules)] + macro_rules! basic_blocks { + (mut) => (body.basic_blocks_mut().iter_enumerated_mut()); + () => (body.basic_blocks().iter_enumerated()); + } + for (bb, data) in basic_blocks!($($mutability)?) { + self.visit_basic_block_data(bb, data); + } + + for scope in &$($mutability)? body.source_scopes { + self.visit_source_scope_data(scope); + } + + self.visit_ty( + $(& $mutability)? body.return_ty(), + TyContext::ReturnTy(SourceInfo::outermost(body.span)) + ); + + for local in body.local_decls.indices() { + self.visit_local_decl(local, & $($mutability)? body.local_decls[local]); + } + + #[allow(unused_macro_rules)] + macro_rules! type_annotations { + (mut) => (body.user_type_annotations.iter_enumerated_mut()); + () => (body.user_type_annotations.iter_enumerated()); + } + + for (index, annotation) in type_annotations!($($mutability)?) { + self.visit_user_type_annotation( + index, annotation + ); + } + + for var_debug_info in &$($mutability)? body.var_debug_info { + self.visit_var_debug_info(var_debug_info); + } + + self.visit_span($(& $mutability)? body.span); + + for const_ in &$($mutability)? body.required_consts { + let location = START_BLOCK.start_location(); + self.visit_constant(const_, location); + } + } + + fn super_basic_block_data(&mut self, + block: BasicBlock, + data: & $($mutability)? BasicBlockData<'tcx>) { + let BasicBlockData { + statements, + terminator, + is_cleanup: _ + } = data; + + let mut index = 0; + for statement in statements { + let location = Location { block, statement_index: index }; + self.visit_statement(statement, location); + index += 1; + } + + if let Some(terminator) = terminator { + let location = Location { block, statement_index: index }; + self.visit_terminator(terminator, location); + } + } + + fn super_source_scope_data( + &mut self, + scope_data: & $($mutability)? SourceScopeData<'tcx>, + ) { + let SourceScopeData { + span, + parent_scope, + inlined, + inlined_parent_scope, + local_data: _, + } = scope_data; + + self.visit_span($(& $mutability)? *span); + if let Some(parent_scope) = parent_scope { + self.visit_source_scope($(& $mutability)? *parent_scope); + } + if let Some((callee, callsite_span)) = inlined { + let location = START_BLOCK.start_location(); + + self.visit_span($(& $mutability)? *callsite_span); + + let ty::Instance { def: callee_def, substs: callee_substs } = callee; + match callee_def { + ty::InstanceDef::Item(_def_id) => {} + + ty::InstanceDef::Intrinsic(_def_id) | + ty::InstanceDef::VTableShim(_def_id) | + ty::InstanceDef::ReifyShim(_def_id) | + ty::InstanceDef::Virtual(_def_id, _) | + ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } | + ty::InstanceDef::DropGlue(_def_id, None) => {} + + ty::InstanceDef::FnPtrShim(_def_id, ty) | + ty::InstanceDef::DropGlue(_def_id, Some(ty)) | + ty::InstanceDef::CloneShim(_def_id, ty) => { + // FIXME(eddyb) use a better `TyContext` here. + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); + } + } + self.visit_substs(callee_substs, location); + } + if let Some(inlined_parent_scope) = inlined_parent_scope { + self.visit_source_scope($(& $mutability)? *inlined_parent_scope); + } + } + + fn super_statement(&mut self, + statement: & $($mutability)? Statement<'tcx>, + location: Location) { + let Statement { + source_info, + kind, + } = statement; + + self.visit_source_info(source_info); + match kind { + StatementKind::Assign( + box (place, rvalue) + ) => { + self.visit_assign(place, rvalue, location); + } + StatementKind::FakeRead(box (_, place)) => { + self.visit_place( + place, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), + location + ); + } + StatementKind::SetDiscriminant { place, .. } => { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::SetDiscriminant), + location + ); + } + StatementKind::Deinit(place) => { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Deinit), + location + ) + } + StatementKind::StorageLive(local) => { + self.visit_local( + $(& $mutability)? *local, + PlaceContext::NonUse(NonUseContext::StorageLive), + location + ); + } + StatementKind::StorageDead(local) => { + self.visit_local( + $(& $mutability)? *local, + PlaceContext::NonUse(NonUseContext::StorageDead), + location + ); + } + StatementKind::Retag(kind, place) => { + self.visit_retag($(& $mutability)? *kind, place, location); + } + StatementKind::AscribeUserType( + box (place, user_ty), + variance + ) => { + self.visit_ascribe_user_ty(place, $(& $mutability)? *variance, user_ty, location); + } + StatementKind::Coverage(coverage) => { + self.visit_coverage( + coverage, + location + ) + } + StatementKind::CopyNonOverlapping(box crate::mir::CopyNonOverlapping{ + src, + dst, + count, + }) => { + self.visit_operand(src, location); + self.visit_operand(dst, location); + self.visit_operand(count, location) + } + StatementKind::Nop => {} + } + } + + fn super_assign(&mut self, + place: &$($mutability)? Place<'tcx>, + rvalue: &$($mutability)? Rvalue<'tcx>, + location: Location) { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Store), + location + ); + self.visit_rvalue(rvalue, location); + } + + fn super_terminator(&mut self, + terminator: &$($mutability)? Terminator<'tcx>, + location: Location) { + let Terminator { source_info, kind } = terminator; + + self.visit_source_info(source_info); + match kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Abort | + TerminatorKind::GeneratorDrop | + TerminatorKind::Unreachable | + TerminatorKind::FalseEdge { .. } | + TerminatorKind::FalseUnwind { .. } => {} + + TerminatorKind::Return => { + // `return` logically moves from the return place `_0`. Note that the place + // cannot be changed by any visitor, though. + let $($mutability)? local = RETURN_PLACE; + self.visit_local( + $(& $mutability)? local, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move), + location, + ); + + assert_eq!( + local, + RETURN_PLACE, + "`MutVisitor` tried to mutate return place of `return` terminator" + ); + } + + TerminatorKind::SwitchInt { + discr, + switch_ty, + targets: _ + } => { + self.visit_operand(discr, location); + self.visit_ty($(& $mutability)? *switch_ty, TyContext::Location(location)); + } + + TerminatorKind::Drop { + place, + target: _, + unwind: _, + } => { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Drop), + location + ); + } + + TerminatorKind::DropAndReplace { + place, + value, + target: _, + unwind: _, + } => { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Drop), + location + ); + self.visit_operand(value, location); + } + + TerminatorKind::Call { + func, + args, + destination, + target: _, + cleanup: _, + from_hir_call: _, + fn_span: _ + } => { + self.visit_operand(func, location); + for arg in args { + self.visit_operand(arg, location); + } + self.visit_place( + destination, + PlaceContext::MutatingUse(MutatingUseContext::Call), + location + ); + } + + TerminatorKind::Assert { + cond, + expected: _, + msg, + target: _, + cleanup: _, + } => { + self.visit_operand(cond, location); + self.visit_assert_message(msg, location); + } + + TerminatorKind::Yield { + value, + resume: _, + resume_arg, + drop: _, + } => { + self.visit_operand(value, location); + self.visit_place( + resume_arg, + PlaceContext::MutatingUse(MutatingUseContext::Yield), + location, + ); + } + + TerminatorKind::InlineAsm { + template: _, + operands, + options: _, + line_spans: _, + destination: _, + cleanup: _, + } => { + for op in operands { + match op { + InlineAsmOperand::In { value, .. } => { + self.visit_operand(value, location); + } + InlineAsmOperand::Out { place: Some(place), .. } => { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::AsmOutput), + location, + ); + } + InlineAsmOperand::InOut { in_value, out_place, .. } => { + self.visit_operand(in_value, location); + if let Some(out_place) = out_place { + self.visit_place( + out_place, + PlaceContext::MutatingUse(MutatingUseContext::AsmOutput), + location, + ); + } + } + InlineAsmOperand::Const { value } + | InlineAsmOperand::SymFn { value } => { + self.visit_constant(value, location); + } + InlineAsmOperand::Out { place: None, .. } + | InlineAsmOperand::SymStatic { def_id: _ } => {} + } + } + } + } + } + + fn super_assert_message(&mut self, + msg: & $($mutability)? AssertMessage<'tcx>, + location: Location) { + use crate::mir::AssertKind::*; + match msg { + BoundsCheck { len, index } => { + self.visit_operand(len, location); + self.visit_operand(index, location); + } + Overflow(_, l, r) => { + self.visit_operand(l, location); + self.visit_operand(r, location); + } + OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => { + self.visit_operand(op, location); + } + ResumedAfterReturn(_) | ResumedAfterPanic(_) => { + // Nothing to visit + } + } + } + + fn super_rvalue(&mut self, + rvalue: & $($mutability)? Rvalue<'tcx>, + location: Location) { + match rvalue { + Rvalue::Use(operand) => { + self.visit_operand(operand, location); + } + + Rvalue::Repeat(value, _) => { + self.visit_operand(value, location); + } + + Rvalue::ThreadLocalRef(_) => {} + + Rvalue::Ref(r, bk, path) => { + self.visit_region($(& $mutability)? *r, location); + let ctx = match bk { + BorrowKind::Shared => PlaceContext::NonMutatingUse( + NonMutatingUseContext::SharedBorrow + ), + BorrowKind::Shallow => PlaceContext::NonMutatingUse( + NonMutatingUseContext::ShallowBorrow + ), + BorrowKind::Unique => PlaceContext::NonMutatingUse( + NonMutatingUseContext::UniqueBorrow + ), + BorrowKind::Mut { .. } => + PlaceContext::MutatingUse(MutatingUseContext::Borrow), + }; + self.visit_place(path, ctx, location); + } + Rvalue::CopyForDeref(place) => { + self.visit_place( + place, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), + location + ); + } + + Rvalue::AddressOf(m, path) => { + let ctx = match m { + Mutability::Mut => PlaceContext::MutatingUse( + MutatingUseContext::AddressOf + ), + Mutability::Not => PlaceContext::NonMutatingUse( + NonMutatingUseContext::AddressOf + ), + }; + self.visit_place(path, ctx, location); + } + + Rvalue::Len(path) => { + self.visit_place( + path, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), + location + ); + } + + Rvalue::Cast(_cast_kind, operand, ty) => { + self.visit_operand(operand, location); + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); + } + + Rvalue::BinaryOp(_bin_op, box(lhs, rhs)) + | Rvalue::CheckedBinaryOp(_bin_op, box(lhs, rhs)) => { + self.visit_operand(lhs, location); + self.visit_operand(rhs, location); + } + + Rvalue::UnaryOp(_un_op, op) => { + self.visit_operand(op, location); + } + + Rvalue::Discriminant(place) => { + self.visit_place( + place, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), + location + ); + } + + Rvalue::NullaryOp(_op, ty) => { + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); + } + + Rvalue::Aggregate(kind, operands) => { + let kind = &$($mutability)? **kind; + match kind { + AggregateKind::Array(ty) => { + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); + } + AggregateKind::Tuple => { + } + AggregateKind::Adt( + _adt_def, + _variant_index, + substs, + _user_substs, + _active_field_index + ) => { + self.visit_substs(substs, location); + } + AggregateKind::Closure( + _, + closure_substs + ) => { + self.visit_substs(closure_substs, location); + } + AggregateKind::Generator( + _, + generator_substs, + _movability, + ) => { + self.visit_substs(generator_substs, location); + } + } + + for operand in operands { + self.visit_operand(operand, location); + } + } + + Rvalue::ShallowInitBox(operand, ty) => { + self.visit_operand(operand, location); + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); + } + } + } + + fn super_operand(&mut self, + operand: & $($mutability)? Operand<'tcx>, + location: Location) { + match operand { + Operand::Copy(place) => { + self.visit_place( + place, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), + location + ); + } + Operand::Move(place) => { + self.visit_place( + place, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move), + location + ); + } + Operand::Constant(constant) => { + self.visit_constant(constant, location); + } + } + } + + fn super_ascribe_user_ty(&mut self, + place: & $($mutability)? Place<'tcx>, + _variance: $(& $mutability)? ty::Variance, + user_ty: & $($mutability)? UserTypeProjection, + location: Location) { + self.visit_place( + place, + PlaceContext::NonUse(NonUseContext::AscribeUserTy), + location + ); + self.visit_user_type_projection(user_ty); + } + + fn super_coverage(&mut self, + _coverage: & $($mutability)? Coverage, + _location: Location) { + } + + fn super_retag(&mut self, + _kind: $(& $mutability)? RetagKind, + place: & $($mutability)? Place<'tcx>, + location: Location) { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Retag), + location, + ); + } + + fn super_local_decl(&mut self, + local: Local, + local_decl: & $($mutability)? LocalDecl<'tcx>) { + let LocalDecl { + mutability: _, + ty, + user_ty, + source_info, + internal: _, + local_info: _, + is_block_tail: _, + } = local_decl; + + self.visit_ty($(& $mutability)? *ty, TyContext::LocalDecl { + local, + source_info: *source_info, + }); + if let Some(user_ty) = user_ty { + for (user_ty, _) in & $($mutability)? user_ty.contents { + self.visit_user_type_projection(user_ty); + } + } + self.visit_source_info(source_info); + } + + fn super_var_debug_info( + &mut self, + var_debug_info: & $($mutability)? VarDebugInfo<'tcx> + ) { + let VarDebugInfo { + name: _, + source_info, + value, + } = var_debug_info; + + self.visit_source_info(source_info); + let location = START_BLOCK.start_location(); + match value { + VarDebugInfoContents::Const(c) => self.visit_constant(c, location), + VarDebugInfoContents::Place(place) => + self.visit_place( + place, + PlaceContext::NonUse(NonUseContext::VarDebugInfo), + location + ), + } + } + + fn super_source_scope( + &mut self, + _scope: $(& $mutability)? SourceScope + ) {} + + fn super_constant( + &mut self, + constant: & $($mutability)? Constant<'tcx>, + location: Location + ) { + let Constant { + span, + user_ty, + literal, + } = constant; + + self.visit_span($(& $mutability)? *span); + drop(user_ty); // no visit method for this + match literal { + ConstantKind::Ty(ct) => self.visit_const($(& $mutability)? *ct, location), + ConstantKind::Val(_, ty) => self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)), + } + } + + fn super_span(&mut self, _span: $(& $mutability)? Span) { + } + + fn super_source_info(&mut self, source_info: & $($mutability)? SourceInfo) { + let SourceInfo { + span, + scope, + } = source_info; + + self.visit_span($(& $mutability)? *span); + self.visit_source_scope($(& $mutability)? *scope); + } + + fn super_user_type_projection( + &mut self, + _ty: & $($mutability)? UserTypeProjection, + ) { + } + + fn super_user_type_annotation( + &mut self, + _index: UserTypeAnnotationIndex, + ty: & $($mutability)? CanonicalUserTypeAnnotation<'tcx>, + ) { + self.visit_span($(& $mutability)? ty.span); + self.visit_ty($(& $mutability)? ty.inferred_ty, TyContext::UserTy(ty.span)); + } + + fn super_ty(&mut self, _ty: $(& $mutability)? Ty<'tcx>) { + } + + fn super_region(&mut self, _region: $(& $mutability)? ty::Region<'tcx>) { + } + + fn super_const(&mut self, _const: $(& $mutability)? ty::Const<'tcx>) { + } + + fn super_substs(&mut self, _substs: & $($mutability)? SubstsRef<'tcx>) { + } + + // Convenience methods + + fn visit_location( + &mut self, + body: &$($mutability)? Body<'tcx>, + location: Location + ) { + #[allow(unused_macro_rules)] + macro_rules! basic_blocks { + (mut) => (body.basic_blocks_mut()); + () => (body.basic_blocks()); + } + let basic_block = & $($mutability)? basic_blocks!($($mutability)?)[location.block]; + if basic_block.statements.len() == location.statement_index { + if let Some(ref $($mutability)? terminator) = basic_block.terminator { + self.visit_terminator(terminator, location) + } + } else { + let statement = & $($mutability)? + basic_block.statements[location.statement_index]; + self.visit_statement(statement, location) + } + } + } + } +} + +macro_rules! visit_place_fns { + (mut) => { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; + + fn super_place( + &mut self, + place: &mut Place<'tcx>, + context: PlaceContext, + location: Location, + ) { + self.visit_local(&mut place.local, context, location); + + if let Some(new_projection) = self.process_projection(&place.projection, location) { + place.projection = self.tcx().intern_place_elems(&new_projection); + } + } + + fn process_projection<'a>( + &mut self, + projection: &'a [PlaceElem<'tcx>], + location: Location, + ) -> Option>> { + let mut projection = Cow::Borrowed(projection); + + for i in 0..projection.len() { + if let Some(&elem) = projection.get(i) { + if let Some(elem) = self.process_projection_elem(elem, location) { + // This converts the borrowed projection into `Cow::Owned(_)` and returns a + // clone of the projection so we can mutate and reintern later. + let vec = projection.to_mut(); + vec[i] = elem; + } + } + } + + match projection { + Cow::Borrowed(_) => None, + Cow::Owned(vec) => Some(vec), + } + } + + fn process_projection_elem( + &mut self, + elem: PlaceElem<'tcx>, + location: Location, + ) -> Option> { + match elem { + PlaceElem::Index(local) => { + let mut new_local = local; + self.visit_local( + &mut new_local, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), + location, + ); + + if new_local == local { None } else { Some(PlaceElem::Index(new_local)) } + } + PlaceElem::Field(field, ty) => { + let mut new_ty = ty; + self.visit_ty(&mut new_ty, TyContext::Location(location)); + if ty != new_ty { Some(PlaceElem::Field(field, new_ty)) } else { None } + } + PlaceElem::Deref + | PlaceElem::ConstantIndex { .. } + | PlaceElem::Subslice { .. } + | PlaceElem::Downcast(..) => None, + } + } + }; + + () => { + fn visit_projection( + &mut self, + place_ref: PlaceRef<'tcx>, + context: PlaceContext, + location: Location, + ) { + self.super_projection(place_ref, context, location); + } + + fn visit_projection_elem( + &mut self, + local: Local, + proj_base: &[PlaceElem<'tcx>], + elem: PlaceElem<'tcx>, + context: PlaceContext, + location: Location, + ) { + self.super_projection_elem(local, proj_base, elem, context, location); + } + + fn super_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + let mut context = context; + + if !place.projection.is_empty() { + if context.is_use() { + // ^ Only change the context if it is a real use, not a "use" in debuginfo. + context = if context.is_mutating_use() { + PlaceContext::MutatingUse(MutatingUseContext::Projection) + } else { + PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) + }; + } + } + + self.visit_local(place.local, context, location); + + self.visit_projection(place.as_ref(), context, location); + } + + fn super_projection( + &mut self, + place_ref: PlaceRef<'tcx>, + context: PlaceContext, + location: Location, + ) { + for (base, elem) in place_ref.iter_projections().rev() { + let base_proj = base.projection; + self.visit_projection_elem(place_ref.local, base_proj, elem, context, location); + } + } + + fn super_projection_elem( + &mut self, + _local: Local, + _proj_base: &[PlaceElem<'tcx>], + elem: PlaceElem<'tcx>, + _context: PlaceContext, + location: Location, + ) { + match elem { + ProjectionElem::Field(_field, ty) => { + self.visit_ty(ty, TyContext::Location(location)); + } + ProjectionElem::Index(local) => { + self.visit_local( + local, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), + location, + ); + } + ProjectionElem::Deref + | ProjectionElem::Subslice { from: _, to: _, from_end: _ } + | ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } + | ProjectionElem::Downcast(_, _) => {} + } + } + }; +} + +make_mir_visitor!(Visitor,); +make_mir_visitor!(MutVisitor, mut); + +pub trait MirVisitable<'tcx> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>); +} + +impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) { + visitor.visit_statement(self, location) + } +} + +impl<'tcx> MirVisitable<'tcx> for Terminator<'tcx> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) { + visitor.visit_terminator(self, location) + } +} + +impl<'tcx> MirVisitable<'tcx> for Option> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) { + visitor.visit_terminator(self.as_ref().unwrap(), location) + } +} + +/// Extra information passed to `visit_ty` and friends to give context +/// about where the type etc appears. +#[derive(Debug)] +pub enum TyContext { + LocalDecl { + /// The index of the local variable we are visiting. + local: Local, + + /// The source location where this local variable was declared. + source_info: SourceInfo, + }, + + /// The inferred type of a user type annotation. + UserTy(Span), + + /// The return type of the function. + ReturnTy(SourceInfo), + + YieldTy(SourceInfo), + + /// A type found at some location. + Location(Location), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum NonMutatingUseContext { + /// Being inspected in some way, like loading a len. + Inspect, + /// Consumed as part of an operand. + Copy, + /// Consumed as part of an operand. + Move, + /// Shared borrow. + SharedBorrow, + /// Shallow borrow. + ShallowBorrow, + /// Unique borrow. + UniqueBorrow, + /// AddressOf for *const pointer. + AddressOf, + /// Used as base for another place, e.g., `x` in `x.y`. Will not mutate the place. + /// For example, the projection `x.y` is not marked as a mutation in these cases: + /// ```ignore (illustrative) + /// z = x.y; + /// f(&x.y); + /// ``` + Projection, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum MutatingUseContext { + /// Appears as LHS of an assignment. + Store, + /// Appears on `SetDiscriminant` + SetDiscriminant, + /// Appears on `Deinit` + Deinit, + /// Output operand of an inline assembly block. + AsmOutput, + /// Destination of a call. + Call, + /// Destination of a yield. + Yield, + /// Being dropped. + Drop, + /// Mutable borrow. + Borrow, + /// AddressOf for *mut pointer. + AddressOf, + /// Used as base for another place, e.g., `x` in `x.y`. Could potentially mutate the place. + /// For example, the projection `x.y` is marked as a mutation in these cases: + /// ```ignore (illustrative) + /// x.y = ...; + /// f(&mut x.y); + /// ``` + Projection, + /// Retagging, a "Stacked Borrows" shadow state operation + Retag, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum NonUseContext { + /// Starting a storage live range. + StorageLive, + /// Ending a storage live range. + StorageDead, + /// User type annotation assertions for NLL. + AscribeUserTy, + /// The data of a user variable, for debug info. + VarDebugInfo, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum PlaceContext { + NonMutatingUse(NonMutatingUseContext), + MutatingUse(MutatingUseContext), + NonUse(NonUseContext), +} + +impl PlaceContext { + /// Returns `true` if this place context represents a drop. + #[inline] + pub fn is_drop(&self) -> bool { + matches!(self, PlaceContext::MutatingUse(MutatingUseContext::Drop)) + } + + /// Returns `true` if this place context represents a borrow. + pub fn is_borrow(&self) -> bool { + matches!( + self, + PlaceContext::NonMutatingUse( + NonMutatingUseContext::SharedBorrow + | NonMutatingUseContext::ShallowBorrow + | NonMutatingUseContext::UniqueBorrow + ) | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) + } + + /// Returns `true` if this place context represents a storage live or storage dead marker. + #[inline] + pub fn is_storage_marker(&self) -> bool { + matches!( + self, + PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) + ) + } + + /// Returns `true` if this place context represents a use that potentially changes the value. + #[inline] + pub fn is_mutating_use(&self) -> bool { + matches!(self, PlaceContext::MutatingUse(..)) + } + + /// Returns `true` if this place context represents a use. + #[inline] + pub fn is_use(&self) -> bool { + !matches!(self, PlaceContext::NonUse(..)) + } + + /// Returns `true` if this place context represents an assignment statement. + pub fn is_place_assignment(&self) -> bool { + matches!( + self, + PlaceContext::MutatingUse( + MutatingUseContext::Store + | MutatingUseContext::Call + | MutatingUseContext::AsmOutput, + ) + ) + } +} -- cgit v1.2.3