| // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT |
| // file at the top-level directory of this distribution and at |
| // http://rust-lang.org/COPYRIGHT. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| use hir::def_id::DefId; |
| use ty::subst::Substs; |
| use ty::{ClosureSubsts, GeneratorSubsts, Region, Ty}; |
| use mir::*; |
| use syntax_pos::Span; |
| |
| // # 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: |
| // |
| // ```rust |
| // fn super_basic_block_data(&mut self, |
| // block: BasicBlock, |
| // data: & $($mutability)* BasicBlockData<'tcx>) { |
| // let BasicBlockData { |
| // ref $($mutability)* statements, |
| // ref $($mutability)* terminator, |
| // is_cleanup: _ |
| // } = *data; |
| // |
| // for statement in statements { |
| // self.visit_statement(block, statement); |
| // } |
| // |
| // ... |
| // } |
| // ``` |
| // |
| // Here we used `let BasicBlockData { <fields> } = *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. |
| |
| 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_mir(&mut self, mir: & $($mutability)* Mir<'tcx>) { |
| self.super_mir(mir); |
| } |
| |
| 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) { |
| self.super_source_scope_data(scope_data); |
| } |
| |
| fn visit_statement(&mut self, |
| block: BasicBlock, |
| statement: & $($mutability)* Statement<'tcx>, |
| location: Location) { |
| self.super_statement(block, statement, location); |
| } |
| |
| fn visit_assign(&mut self, |
| block: BasicBlock, |
| place: & $($mutability)* Place<'tcx>, |
| rvalue: & $($mutability)* Rvalue<'tcx>, |
| location: Location) { |
| self.super_assign(block, place, rvalue, location); |
| } |
| |
| fn visit_terminator(&mut self, |
| block: BasicBlock, |
| terminator: & $($mutability)* Terminator<'tcx>, |
| location: Location) { |
| self.super_terminator(block, terminator, location); |
| } |
| |
| fn visit_terminator_kind(&mut self, |
| block: BasicBlock, |
| kind: & $($mutability)* TerminatorKind<'tcx>, |
| location: Location) { |
| self.super_terminator_kind(block, kind, 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<'tcx>, |
| location: Location) { |
| self.super_ascribe_user_ty(place, variance, user_ty, location); |
| } |
| |
| fn visit_retag(&mut self, |
| fn_entry: & $($mutability)* bool, |
| place: & $($mutability)* Place<'tcx>, |
| location: Location) { |
| self.super_retag(fn_entry, place, location); |
| } |
| |
| fn visit_place(&mut self, |
| place: & $($mutability)* Place<'tcx>, |
| context: PlaceContext<'tcx>, |
| location: Location) { |
| self.super_place(place, context, location); |
| } |
| |
| fn visit_static(&mut self, |
| static_: & $($mutability)* Static<'tcx>, |
| context: PlaceContext<'tcx>, |
| location: Location) { |
| self.super_static(static_, context, location); |
| } |
| |
| fn visit_projection(&mut self, |
| place: & $($mutability)* PlaceProjection<'tcx>, |
| context: PlaceContext<'tcx>, |
| location: Location) { |
| self.super_projection(place, context, location); |
| } |
| |
| fn visit_projection_elem(&mut self, |
| place: & $($mutability)* PlaceElem<'tcx>, |
| location: Location) { |
| self.super_projection_elem(place, location); |
| } |
| |
| fn visit_branch(&mut self, |
| source: BasicBlock, |
| target: BasicBlock) { |
| self.super_branch(source, target); |
| } |
| |
| fn visit_constant(&mut self, |
| constant: & $($mutability)* Constant<'tcx>, |
| location: Location) { |
| self.super_constant(constant, location); |
| } |
| |
| fn visit_def_id(&mut self, |
| def_id: & $($mutability)* DefId, |
| _: Location) { |
| self.super_def_id(def_id); |
| } |
| |
| 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<'tcx>, |
| ) { |
| self.super_user_type_projection(ty); |
| } |
| |
| fn visit_user_type_annotation( |
| &mut self, |
| ty: & $($mutability)* UserTypeAnnotation<'tcx>, |
| ) { |
| self.super_user_type_annotation(ty); |
| } |
| |
| fn visit_region(&mut self, |
| region: & $($mutability)* ty::Region<'tcx>, |
| _: Location) { |
| self.super_region(region); |
| } |
| |
| fn visit_const(&mut self, |
| constant: & $($mutability)* &'tcx ty::Const<'tcx>, |
| _: Location) { |
| self.super_const(constant); |
| } |
| |
| fn visit_substs(&mut self, |
| substs: & $($mutability)* &'tcx Substs<'tcx>, |
| _: Location) { |
| self.super_substs(substs); |
| } |
| |
| fn visit_closure_substs(&mut self, |
| substs: & $($mutability)* ClosureSubsts<'tcx>, |
| _: Location) { |
| self.super_closure_substs(substs); |
| } |
| |
| fn visit_generator_substs(&mut self, |
| substs: & $($mutability)* GeneratorSubsts<'tcx>, |
| _: Location) { |
| self.super_generator_substs(substs); |
| } |
| |
| fn visit_local_decl(&mut self, |
| local: Local, |
| local_decl: & $($mutability)* LocalDecl<'tcx>) { |
| self.super_local_decl(local, local_decl); |
| } |
| |
| fn visit_local(&mut self, |
| _local: & $($mutability)* Local, |
| _context: PlaceContext<'tcx>, |
| _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_mir(&mut self, |
| mir: & $($mutability)* Mir<'tcx>) { |
| if let Some(yield_ty) = &$($mutability)* mir.yield_ty { |
| self.visit_ty(yield_ty, TyContext::YieldTy(SourceInfo { |
| span: mir.span, |
| scope: OUTERMOST_SOURCE_SCOPE, |
| })); |
| } |
| |
| // for best performance, we want to use an iterator rather |
| // than a for-loop, to avoid calling Mir::invalidate for |
| // each basic block. |
| macro_rules! basic_blocks { |
| (mut) => (mir.basic_blocks_mut().iter_enumerated_mut()); |
| () => (mir.basic_blocks().iter_enumerated()); |
| }; |
| for (bb, data) in basic_blocks!($($mutability)*) { |
| self.visit_basic_block_data(bb, data); |
| } |
| |
| for scope in &$($mutability)* mir.source_scopes { |
| self.visit_source_scope_data(scope); |
| } |
| |
| self.visit_ty(&$($mutability)* mir.return_ty(), TyContext::ReturnTy(SourceInfo { |
| span: mir.span, |
| scope: OUTERMOST_SOURCE_SCOPE, |
| })); |
| |
| for local in mir.local_decls.indices() { |
| self.visit_local_decl(local, & $($mutability)* mir.local_decls[local]); |
| } |
| |
| self.visit_span(&$($mutability)* mir.span); |
| } |
| |
| fn super_basic_block_data(&mut self, |
| block: BasicBlock, |
| data: & $($mutability)* BasicBlockData<'tcx>) { |
| let BasicBlockData { |
| ref $($mutability)* statements, |
| ref $($mutability)* terminator, |
| is_cleanup: _ |
| } = *data; |
| |
| let mut index = 0; |
| for statement in statements { |
| let location = Location { block: block, statement_index: index }; |
| self.visit_statement(block, statement, location); |
| index += 1; |
| } |
| |
| if let Some(ref $($mutability)* terminator) = *terminator { |
| let location = Location { block: block, statement_index: index }; |
| self.visit_terminator(block, terminator, location); |
| } |
| } |
| |
| fn super_source_scope_data(&mut self, |
| scope_data: & $($mutability)* SourceScopeData) { |
| let SourceScopeData { |
| ref $($mutability)* span, |
| ref $($mutability)* parent_scope, |
| } = *scope_data; |
| |
| self.visit_span(span); |
| if let Some(ref $($mutability)* parent_scope) = *parent_scope { |
| self.visit_source_scope(parent_scope); |
| } |
| } |
| |
| fn super_statement(&mut self, |
| block: BasicBlock, |
| statement: & $($mutability)* Statement<'tcx>, |
| location: Location) { |
| let Statement { |
| ref $($mutability)* source_info, |
| ref $($mutability)* kind, |
| } = *statement; |
| |
| self.visit_source_info(source_info); |
| match *kind { |
| StatementKind::Assign(ref $($mutability)* place, |
| ref $($mutability)* rvalue) => { |
| self.visit_assign(block, place, rvalue, location); |
| } |
| StatementKind::FakeRead(_, ref $($mutability)* place) => { |
| self.visit_place( |
| place, |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), |
| location |
| ); |
| } |
| StatementKind::SetDiscriminant{ ref $($mutability)* place, .. } => { |
| self.visit_place( |
| place, |
| PlaceContext::MutatingUse(MutatingUseContext::Store), |
| location |
| ); |
| } |
| StatementKind::EscapeToRaw(ref $($mutability)* op) => { |
| self.visit_operand(op, location); |
| } |
| StatementKind::StorageLive(ref $($mutability)* local) => { |
| self.visit_local( |
| local, |
| PlaceContext::NonUse(NonUseContext::StorageLive), |
| location |
| ); |
| } |
| StatementKind::StorageDead(ref $($mutability)* local) => { |
| self.visit_local( |
| local, |
| PlaceContext::NonUse(NonUseContext::StorageDead), |
| location |
| ); |
| } |
| StatementKind::InlineAsm { ref $($mutability)* outputs, |
| ref $($mutability)* inputs, |
| asm: _ } => { |
| for output in & $($mutability)* outputs[..] { |
| self.visit_place( |
| output, |
| PlaceContext::MutatingUse(MutatingUseContext::AsmOutput), |
| location |
| ); |
| } |
| for (span, input) in & $($mutability)* inputs[..] { |
| self.visit_span(span); |
| self.visit_operand(input, location); |
| } |
| } |
| StatementKind::Retag { ref $($mutability)* fn_entry, |
| ref $($mutability)* place } => { |
| self.visit_retag(fn_entry, place, location); |
| } |
| StatementKind::AscribeUserType( |
| ref $($mutability)* place, |
| ref $($mutability)* variance, |
| ref $($mutability)* user_ty, |
| ) => { |
| self.visit_ascribe_user_ty(place, variance, user_ty, location); |
| } |
| StatementKind::Nop => {} |
| } |
| } |
| |
| fn super_assign(&mut self, |
| _block: BasicBlock, |
| 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, |
| block: BasicBlock, |
| terminator: &$($mutability)* Terminator<'tcx>, |
| location: Location) { |
| let Terminator { |
| ref $($mutability)* source_info, |
| ref $($mutability)* kind, |
| } = *terminator; |
| |
| self.visit_source_info(source_info); |
| self.visit_terminator_kind(block, kind, location); |
| } |
| |
| fn super_terminator_kind(&mut self, |
| block: BasicBlock, |
| kind: & $($mutability)* TerminatorKind<'tcx>, |
| source_location: Location) { |
| match *kind { |
| TerminatorKind::Goto { target } => { |
| self.visit_branch(block, target); |
| } |
| |
| TerminatorKind::SwitchInt { ref $($mutability)* discr, |
| ref $($mutability)* switch_ty, |
| values: _, |
| ref targets } => { |
| self.visit_operand(discr, source_location); |
| self.visit_ty(switch_ty, TyContext::Location(source_location)); |
| for &target in targets { |
| self.visit_branch(block, target); |
| } |
| } |
| |
| TerminatorKind::Resume | |
| TerminatorKind::Abort | |
| TerminatorKind::Return | |
| TerminatorKind::GeneratorDrop | |
| TerminatorKind::Unreachable => { |
| } |
| |
| TerminatorKind::Drop { ref $($mutability)* location, |
| target, |
| unwind } => { |
| self.visit_place( |
| location, |
| PlaceContext::MutatingUse(MutatingUseContext::Drop), |
| source_location |
| ); |
| self.visit_branch(block, target); |
| unwind.map(|t| self.visit_branch(block, t)); |
| } |
| |
| TerminatorKind::DropAndReplace { ref $($mutability)* location, |
| ref $($mutability)* value, |
| target, |
| unwind } => { |
| self.visit_place( |
| location, |
| PlaceContext::MutatingUse(MutatingUseContext::Drop), |
| source_location |
| ); |
| self.visit_operand(value, source_location); |
| self.visit_branch(block, target); |
| unwind.map(|t| self.visit_branch(block, t)); |
| } |
| |
| TerminatorKind::Call { ref $($mutability)* func, |
| ref $($mutability)* args, |
| ref $($mutability)* destination, |
| cleanup, |
| from_hir_call: _, } => { |
| self.visit_operand(func, source_location); |
| for arg in args { |
| self.visit_operand(arg, source_location); |
| } |
| if let Some((ref $($mutability)* destination, target)) = *destination { |
| self.visit_place( |
| destination, |
| PlaceContext::MutatingUse(MutatingUseContext::Call), |
| source_location |
| ); |
| self.visit_branch(block, target); |
| } |
| cleanup.map(|t| self.visit_branch(block, t)); |
| } |
| |
| TerminatorKind::Assert { ref $($mutability)* cond, |
| expected: _, |
| ref $($mutability)* msg, |
| target, |
| cleanup } => { |
| self.visit_operand(cond, source_location); |
| self.visit_assert_message(msg, source_location); |
| self.visit_branch(block, target); |
| cleanup.map(|t| self.visit_branch(block, t)); |
| } |
| |
| TerminatorKind::Yield { ref $($mutability)* value, |
| resume, |
| drop } => { |
| self.visit_operand(value, source_location); |
| self.visit_branch(block, resume); |
| drop.map(|t| self.visit_branch(block, t)); |
| } |
| |
| TerminatorKind::FalseEdges { real_target, ref imaginary_targets} => { |
| self.visit_branch(block, real_target); |
| for target in imaginary_targets { |
| self.visit_branch(block, *target); |
| } |
| } |
| |
| TerminatorKind::FalseUnwind { real_target, unwind } => { |
| self.visit_branch(block, real_target); |
| if let Some(unwind) = unwind { |
| self.visit_branch(block, unwind); |
| } |
| } |
| } |
| } |
| |
| fn super_assert_message(&mut self, |
| msg: & $($mutability)* AssertMessage<'tcx>, |
| location: Location) { |
| use mir::interpret::EvalErrorKind::*; |
| if let BoundsCheck { |
| ref $($mutability)* len, |
| ref $($mutability)* index |
| } = *msg { |
| self.visit_operand(len, location); |
| self.visit_operand(index, location); |
| } |
| } |
| |
| fn super_rvalue(&mut self, |
| rvalue: & $($mutability)* Rvalue<'tcx>, |
| location: Location) { |
| match *rvalue { |
| Rvalue::Use(ref $($mutability)* operand) => { |
| self.visit_operand(operand, location); |
| } |
| |
| Rvalue::Repeat(ref $($mutability)* value, _) => { |
| self.visit_operand(value, location); |
| } |
| |
| Rvalue::Ref(ref $($mutability)* r, bk, ref $($mutability)* path) => { |
| self.visit_region(r, location); |
| let ctx = match bk { |
| BorrowKind::Shared => PlaceContext::NonMutatingUse( |
| NonMutatingUseContext::SharedBorrow(*r) |
| ), |
| BorrowKind::Shallow => PlaceContext::NonMutatingUse( |
| NonMutatingUseContext::ShallowBorrow(*r) |
| ), |
| BorrowKind::Unique => PlaceContext::NonMutatingUse( |
| NonMutatingUseContext::UniqueBorrow(*r) |
| ), |
| BorrowKind::Mut { .. } => |
| PlaceContext::MutatingUse(MutatingUseContext::Borrow(*r)), |
| }; |
| self.visit_place(path, ctx, location); |
| } |
| |
| Rvalue::Len(ref $($mutability)* path) => { |
| self.visit_place( |
| path, |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), |
| location |
| ); |
| } |
| |
| Rvalue::Cast(_cast_kind, |
| ref $($mutability)* operand, |
| ref $($mutability)* ty) => { |
| self.visit_operand(operand, location); |
| self.visit_ty(ty, TyContext::Location(location)); |
| } |
| |
| Rvalue::BinaryOp(_bin_op, |
| ref $($mutability)* lhs, |
| ref $($mutability)* rhs) | |
| Rvalue::CheckedBinaryOp(_bin_op, |
| ref $($mutability)* lhs, |
| ref $($mutability)* rhs) => { |
| self.visit_operand(lhs, location); |
| self.visit_operand(rhs, location); |
| } |
| |
| Rvalue::UnaryOp(_un_op, ref $($mutability)* op) => { |
| self.visit_operand(op, location); |
| } |
| |
| Rvalue::Discriminant(ref $($mutability)* place) => { |
| self.visit_place( |
| place, |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), |
| location |
| ); |
| } |
| |
| Rvalue::NullaryOp(_op, ref $($mutability)* ty) => { |
| self.visit_ty(ty, TyContext::Location(location)); |
| } |
| |
| Rvalue::Aggregate(ref $($mutability)* kind, |
| ref $($mutability)* operands) => { |
| let kind = &$($mutability)* **kind; |
| match *kind { |
| AggregateKind::Array(ref $($mutability)* ty) => { |
| self.visit_ty(ty, TyContext::Location(location)); |
| } |
| AggregateKind::Tuple => { |
| } |
| AggregateKind::Adt(_adt_def, |
| _variant_index, |
| ref $($mutability)* substs, |
| _user_substs, |
| _active_field_index) => { |
| self.visit_substs(substs, location); |
| } |
| AggregateKind::Closure(ref $($mutability)* def_id, |
| ref $($mutability)* closure_substs) => { |
| self.visit_def_id(def_id, location); |
| self.visit_closure_substs(closure_substs, location); |
| } |
| AggregateKind::Generator(ref $($mutability)* def_id, |
| ref $($mutability)* generator_substs, |
| _movability) => { |
| self.visit_def_id(def_id, location); |
| self.visit_generator_substs(generator_substs, location); |
| } |
| } |
| |
| for operand in operands { |
| self.visit_operand(operand, location); |
| } |
| } |
| } |
| } |
| |
| fn super_operand(&mut self, |
| operand: & $($mutability)* Operand<'tcx>, |
| location: Location) { |
| match *operand { |
| Operand::Copy(ref $($mutability)* place) => { |
| self.visit_place( |
| place, |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), |
| location |
| ); |
| } |
| Operand::Move(ref $($mutability)* place) => { |
| self.visit_place( |
| place, |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Move), |
| location |
| ); |
| } |
| Operand::Constant(ref $($mutability)* 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<'tcx>, |
| location: Location) { |
| self.visit_place( |
| place, |
| PlaceContext::NonUse(NonUseContext::AscribeUserTy), |
| location |
| ); |
| self.visit_user_type_projection(user_ty); |
| } |
| |
| fn super_retag(&mut self, |
| _fn_entry: & $($mutability)* bool, |
| place: & $($mutability)* Place<'tcx>, |
| location: Location) { |
| self.visit_place( |
| place, |
| PlaceContext::MutatingUse(MutatingUseContext::Retag), |
| location, |
| ); |
| } |
| |
| fn super_place(&mut self, |
| place: & $($mutability)* Place<'tcx>, |
| context: PlaceContext<'tcx>, |
| location: Location) { |
| match *place { |
| Place::Local(ref $($mutability)* local) => { |
| self.visit_local(local, context, location); |
| } |
| Place::Static(ref $($mutability)* static_) => { |
| self.visit_static(static_, context, location); |
| } |
| Place::Promoted(ref $($mutability)* promoted) => { |
| self.visit_ty(& $($mutability)* promoted.1, TyContext::Location(location)); |
| }, |
| Place::Projection(ref $($mutability)* proj) => { |
| self.visit_projection(proj, context, location); |
| } |
| } |
| } |
| |
| fn super_static(&mut self, |
| static_: & $($mutability)* Static<'tcx>, |
| _context: PlaceContext<'tcx>, |
| location: Location) { |
| let Static { |
| ref $($mutability)* def_id, |
| ref $($mutability)* ty, |
| } = *static_; |
| self.visit_def_id(def_id, location); |
| self.visit_ty(ty, TyContext::Location(location)); |
| } |
| |
| fn super_projection(&mut self, |
| proj: & $($mutability)* PlaceProjection<'tcx>, |
| context: PlaceContext<'tcx>, |
| location: Location) { |
| let Projection { |
| ref $($mutability)* base, |
| ref $($mutability)* elem, |
| } = *proj; |
| let context = if context.is_mutating_use() { |
| PlaceContext::MutatingUse(MutatingUseContext::Projection) |
| } else { |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) |
| }; |
| self.visit_place(base, context, location); |
| self.visit_projection_elem(elem, location); |
| } |
| |
| fn super_projection_elem(&mut self, |
| proj: & $($mutability)* PlaceElem<'tcx>, |
| location: Location) { |
| match *proj { |
| ProjectionElem::Deref => { |
| } |
| ProjectionElem::Subslice { from: _, to: _ } => { |
| } |
| ProjectionElem::Field(_field, ref $($mutability)* ty) => { |
| self.visit_ty(ty, TyContext::Location(location)); |
| } |
| ProjectionElem::Index(ref $($mutability)* local) => { |
| self.visit_local( |
| local, |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), |
| location |
| ); |
| } |
| ProjectionElem::ConstantIndex { offset: _, |
| min_length: _, |
| from_end: _ } => { |
| } |
| ProjectionElem::Downcast(_adt_def, _variant_index) => { |
| } |
| } |
| } |
| |
| fn super_local_decl(&mut self, |
| local: Local, |
| local_decl: & $($mutability)* LocalDecl<'tcx>) { |
| let LocalDecl { |
| mutability: _, |
| ref $($mutability)* ty, |
| ref $($mutability)* user_ty, |
| name: _, |
| ref $($mutability)* source_info, |
| ref $($mutability)* visibility_scope, |
| internal: _, |
| is_user_variable: _, |
| is_block_tail: _, |
| } = *local_decl; |
| |
| self.visit_ty(ty, TyContext::LocalDecl { |
| local, |
| source_info: *source_info, |
| }); |
| for (user_ty, _) in & $($mutability)* user_ty.contents { |
| self.visit_user_type_projection(user_ty); |
| } |
| self.visit_source_info(source_info); |
| self.visit_source_scope(visibility_scope); |
| } |
| |
| fn super_source_scope(&mut self, |
| _scope: & $($mutability)* SourceScope) { |
| } |
| |
| fn super_branch(&mut self, |
| _source: BasicBlock, |
| _target: BasicBlock) { |
| } |
| |
| fn super_constant(&mut self, |
| constant: & $($mutability)* Constant<'tcx>, |
| location: Location) { |
| let Constant { |
| ref $($mutability)* span, |
| ref $($mutability)* ty, |
| ref $($mutability)* user_ty, |
| ref $($mutability)* literal, |
| } = *constant; |
| |
| self.visit_span(span); |
| self.visit_ty(ty, TyContext::Location(location)); |
| drop(user_ty); // no visit method for this |
| self.visit_const(literal, location); |
| } |
| |
| fn super_def_id(&mut self, _def_id: & $($mutability)* DefId) { |
| } |
| |
| fn super_span(&mut self, _span: & $($mutability)* Span) { |
| } |
| |
| fn super_source_info(&mut self, source_info: & $($mutability)* SourceInfo) { |
| let SourceInfo { |
| ref $($mutability)* span, |
| ref $($mutability)* scope, |
| } = *source_info; |
| |
| self.visit_span(span); |
| self.visit_source_scope(scope); |
| } |
| |
| fn super_user_type_projection( |
| &mut self, |
| ty: & $($mutability)* UserTypeProjection<'tcx>, |
| ) { |
| let UserTypeProjection { |
| ref $($mutability)* base, |
| projs: _, // Note: Does not visit projection elems! |
| } = *ty; |
| self.visit_user_type_annotation(base); |
| } |
| |
| fn super_user_type_annotation( |
| &mut self, |
| _ty: & $($mutability)* UserTypeAnnotation<'tcx>, |
| ) { |
| } |
| |
| 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)* &'tcx ty::Const<'tcx>) { |
| } |
| |
| fn super_substs(&mut self, _substs: & $($mutability)* &'tcx Substs<'tcx>) { |
| } |
| |
| fn super_generator_substs(&mut self, |
| _substs: & $($mutability)* GeneratorSubsts<'tcx>) { |
| } |
| |
| fn super_closure_substs(&mut self, |
| _substs: & $($mutability)* ClosureSubsts<'tcx>) { |
| } |
| |
| // Convenience methods |
| |
| fn visit_location(&mut self, mir: & $($mutability)* Mir<'tcx>, location: Location) { |
| let basic_block = & $($mutability)* mir[location.block]; |
| if basic_block.statements.len() == location.statement_index { |
| if let Some(ref $($mutability)* terminator) = basic_block.terminator { |
| self.visit_terminator(location.block, terminator, location) |
| } |
| } else { |
| let statement = & $($mutability)* |
| basic_block.statements[location.statement_index]; |
| self.visit_statement(location.block, statement, location) |
| } |
| } |
| } |
| } |
| } |
| |
| 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(location.block, self, location) |
| } |
| } |
| |
| impl<'tcx> MirVisitable<'tcx> for Terminator<'tcx> { |
| fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) |
| { |
| visitor.visit_terminator(location.block, self, location) |
| } |
| } |
| |
| impl<'tcx> MirVisitable<'tcx> for Option<Terminator<'tcx>> { |
| fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) |
| { |
| visitor.visit_terminator(location.block, self.as_ref().unwrap(), location) |
| } |
| } |
| |
| /// Extra information passed to `visit_ty` and friends to give context |
| /// about where the type etc appears. |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] |
| 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 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<'tcx> { |
| /// 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(Region<'tcx>), |
| /// Shallow borrow. |
| ShallowBorrow(Region<'tcx>), |
| /// Unique borrow. |
| UniqueBorrow(Region<'tcx>), |
| /// 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: |
| /// |
| /// z = x.y; |
| /// f(&x.y); |
| /// |
| Projection, |
| } |
| |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| pub enum MutatingUseContext<'tcx> { |
| /// Appears as LHS of an assignment. |
| Store, |
| /// Can often be treated as a `Store`, but needs to be separate because |
| /// ASM is allowed to read outputs as well, so a `Store`-`AsmOutput` sequence |
| /// cannot be simplified the way a `Store`-`Store` can be. |
| AsmOutput, |
| /// Destination of a call. |
| Call, |
| /// Being dropped. |
| Drop, |
| /// Mutable borrow. |
| Borrow(Region<'tcx>), |
| /// 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: |
| /// |
| /// 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, |
| } |
| |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| pub enum PlaceContext<'tcx> { |
| NonMutatingUse(NonMutatingUseContext<'tcx>), |
| MutatingUse(MutatingUseContext<'tcx>), |
| NonUse(NonUseContext), |
| } |
| |
| impl<'tcx> PlaceContext<'tcx> { |
| /// Returns `true` if this place context represents a drop. |
| pub fn is_drop(&self) -> bool { |
| match *self { |
| PlaceContext::MutatingUse(MutatingUseContext::Drop) => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns `true` if this place context represents a borrow. |
| pub fn is_borrow(&self) -> bool { |
| match *self { |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(..)) | |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(..)) | |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(..)) | |
| PlaceContext::MutatingUse(MutatingUseContext::Borrow(..)) => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns `true` if this place context represents a storage live or storage dead marker. |
| pub fn is_storage_marker(&self) -> bool { |
| match *self { |
| PlaceContext::NonUse(NonUseContext::StorageLive) | |
| PlaceContext::NonUse(NonUseContext::StorageDead) => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns `true` if this place context represents a storage live marker. |
| pub fn is_storage_live_marker(&self) -> bool { |
| match *self { |
| PlaceContext::NonUse(NonUseContext::StorageLive) => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns `true` if this place context represents a storage dead marker. |
| pub fn is_storage_dead_marker(&self) -> bool { |
| match *self { |
| PlaceContext::NonUse(NonUseContext::StorageDead) => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns `true` if this place context represents a use that potentially changes the value. |
| pub fn is_mutating_use(&self) -> bool { |
| match *self { |
| PlaceContext::MutatingUse(..) => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns `true` if this place context represents a use that does not change the value. |
| pub fn is_nonmutating_use(&self) -> bool { |
| match *self { |
| PlaceContext::NonMutatingUse(..) => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns `true` if this place context represents a use. |
| pub fn is_use(&self) -> bool { |
| match *self { |
| PlaceContext::NonUse(..) => false, |
| _ => true, |
| } |
| } |
| |
| /// Returns `true` if this place context represents an assignment statement. |
| pub fn is_place_assignment(&self) -> bool { |
| match *self { |
| PlaceContext::MutatingUse(MutatingUseContext::Store) | |
| PlaceContext::MutatingUse(MutatingUseContext::Call) | |
| PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) => true, |
| _ => false, |
| } |
| } |
| } |