| //! # The MIR Visitor | |
| //! | |
| //! ## Overview | |
| //! | |
| //! There are two visitors, one for immutable and one for mutable references, | |
| //! but both are generated by the `make_mir_visitor` 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 to override `visit_foo` for types you 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 { <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. | |
| use crate::mir::*; | |
| use crate::ty::CanonicalUserTypeAnnotation; | |
| 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); | |
| } | |
| extra_body_methods!($($mutability)?); | |
| 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_debuginfo( | |
| &mut self, | |
| stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>, | |
| location: Location | |
| ) { | |
| self.super_statement_debuginfo(stmt_debuginfo, location); | |
| } | |
| 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, | |
| kind: & $($mutability)? coverage::CoverageKind, | |
| location: Location, | |
| ) { | |
| self.super_coverage(kind, location); | |
| } | |
| fn visit_place( | |
| &mut self, | |
| place: & $($mutability)? Place<'tcx>, | |
| context: PlaceContext, | |
| location: Location, | |
| ) { | |
| self.super_place(place, context, location); | |
| } | |
| visit_place_fns!($($mutability)?); | |
| /// This is called for every constant in the MIR body and every `required_consts` | |
| /// (i.e., including consts that have been dead-code-eliminated). | |
| fn visit_const_operand( | |
| &mut self, | |
| constant: & $($mutability)? ConstOperand<'tcx>, | |
| location: Location, | |
| ) { | |
| self.super_const_operand(constant, location); | |
| } | |
| fn visit_ty_const( | |
| &mut self, | |
| ct: $( & $mutability)? ty::Const<'tcx>, | |
| location: Location, | |
| ) { | |
| self.super_ty_const(ct, 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_args( | |
| &mut self, | |
| args: & $($mutability)? GenericArgsRef<'tcx>, | |
| _: Location, | |
| ) { | |
| self.super_args(args); | |
| } | |
| 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, | |
| ) { | |
| self.super_local(local, context, 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>, | |
| ) { | |
| super_body!(self, body, $($mutability, true)?); | |
| } | |
| fn super_basic_block_data( | |
| &mut self, | |
| block: BasicBlock, | |
| data: & $($mutability)? BasicBlockData<'tcx>) | |
| { | |
| let BasicBlockData { | |
| statements, | |
| after_last_stmt_debuginfos, | |
| 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; | |
| } | |
| let location = Location { block, statement_index: index }; | |
| for debuginfo in after_last_stmt_debuginfos as & $($mutability)? [_] { | |
| self.visit_statement_debuginfo(debuginfo, location); | |
| } | |
| if let Some(terminator) = terminator { | |
| 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 = Location::START; | |
| self.visit_span($(& $mutability)? *callsite_span); | |
| let ty::Instance { def: callee_def, args: callee_args } = callee; | |
| match callee_def { | |
| ty::InstanceKind::Item(_def_id) => {} | |
| ty::InstanceKind::Intrinsic(_def_id) | |
| | ty::InstanceKind::LlvmIntrinsic(_def_id) | |
| | ty::InstanceKind::Shim(ty::ShimKind::VTable(_def_id)) | |
| | ty::InstanceKind::Shim(ty::ShimKind::Reify(_def_id, _)) | |
| | ty::InstanceKind::Virtual(_def_id, _) | |
| | ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(_def_id)) | |
| | ty::InstanceKind::Shim(ty::ShimKind::ClosureOnce { call_once: _def_id, closure: _, track_caller: _ }) | |
| | ty::InstanceKind::Shim(ty::ShimKind::ConstructCoroutineInClosure { | |
| coroutine_closure_def_id: _def_id, | |
| receiver_by_ref: _, | |
| }) | |
| | ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_def_id, None)) => {} | |
| ty::InstanceKind::Shim(ty::ShimKind::FnPtr(_def_id, ty)) | |
| | ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_def_id, Some(ty))) | |
| | ty::InstanceKind::Shim(ty::ShimKind::Clone(_def_id, ty)) | |
| | ty::InstanceKind::Shim(ty::ShimKind::FnPtrAddr(_def_id, ty)) | |
| | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlue(_def_id, ty)) | |
| | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlueCtor(_def_id, ty)) => { | |
| // FIXME(eddyb) use a better `TyContext` here. | |
| self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); | |
| } | |
| ty::InstanceKind::Shim(ty::ShimKind::FutureDropPoll(_def_id, proxy_ty, impl_ty)) => { | |
| self.visit_ty($(& $mutability)? *proxy_ty, TyContext::Location(location)); | |
| self.visit_ty($(& $mutability)? *impl_ty, TyContext::Location(location)); | |
| } | |
| } | |
| self.visit_args(callee_args, location); | |
| } | |
| if let Some(inlined_parent_scope) = inlined_parent_scope { | |
| self.visit_source_scope($(& $mutability)? *inlined_parent_scope); | |
| } | |
| } | |
| fn super_statement_debuginfo( | |
| &mut self, | |
| stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>, | |
| location: Location | |
| ) { | |
| match stmt_debuginfo { | |
| StmtDebugInfo::AssignRef(local, place) => { | |
| self.visit_local( | |
| $(& $mutability)? *local, | |
| PlaceContext::NonUse(NonUseContext::VarDebugInfo), | |
| location | |
| ); | |
| self.visit_place( | |
| place, | |
| PlaceContext::NonUse(NonUseContext::VarDebugInfo), | |
| location | |
| ); | |
| }, | |
| StmtDebugInfo::InvalidAssign(local) => { | |
| self.visit_local( | |
| $(& $mutability)? *local, | |
| PlaceContext::NonUse(NonUseContext::VarDebugInfo), | |
| location | |
| ); | |
| } | |
| } | |
| } | |
| fn super_statement( | |
| &mut self, | |
| statement: & $($mutability)? Statement<'tcx>, | |
| location: Location | |
| ) { | |
| let Statement { source_info, kind, debuginfos } = statement; | |
| self.visit_source_info(source_info); | |
| for debuginfo in debuginfos as & $($mutability)? [_] { | |
| self.visit_statement_debuginfo(debuginfo, location); | |
| } | |
| match kind { | |
| StatementKind::Assign((place, rvalue)) => { | |
| self.visit_assign(place, rvalue, location); | |
| } | |
| StatementKind::FakeRead((_, place)) => { | |
| self.visit_place( | |
| place, | |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), | |
| location | |
| ); | |
| } | |
| StatementKind::SetDiscriminant { place, .. } => { | |
| self.visit_place( | |
| place, | |
| PlaceContext::MutatingUse(MutatingUseContext::SetDiscriminant), | |
| 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::PlaceMention(place) => { | |
| self.visit_place( | |
| place, | |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::PlaceMention), | |
| location | |
| ); | |
| } | |
| StatementKind::AscribeUserType((place, user_ty), variance) => { | |
| self.visit_ascribe_user_ty( | |
| place, | |
| $(& $mutability)? *variance, | |
| user_ty, | |
| location | |
| ); | |
| } | |
| StatementKind::Coverage(coverage) => { | |
| self.visit_coverage( | |
| coverage, | |
| location | |
| ) | |
| } | |
| StatementKind::Intrinsic(intrinsic) => { | |
| match intrinsic { | |
| NonDivergingIntrinsic::Assume(op) => self.visit_operand(op, location), | |
| NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { | |
| src, | |
| dst, | |
| count | |
| }) => { | |
| self.visit_operand(src, location); | |
| self.visit_operand(dst, location); | |
| self.visit_operand(count, location); | |
| } | |
| } | |
| } | |
| StatementKind::BackwardIncompatibleDropHint { place, .. } => { | |
| self.visit_place( | |
| place, | |
| PlaceContext::NonUse(NonUseContext::BackwardIncompatibleDropHint), | |
| location | |
| ); | |
| } | |
| StatementKind::ConstEvalCounter => {} | |
| 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, attributes: _ } = terminator; | |
| self.visit_source_info(source_info); | |
| match kind { | |
| TerminatorKind::Goto { .. } | |
| | TerminatorKind::UnwindResume | |
| | TerminatorKind::UnwindTerminate(_) | |
| | TerminatorKind::CoroutineDrop | |
| | 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, targets: _ } => { | |
| self.visit_operand(discr, location); | |
| } | |
| TerminatorKind::Drop { | |
| place, | |
| target: _, | |
| unwind: _, | |
| replace: _, | |
| drop: _, | |
| } => { | |
| self.visit_place( | |
| place, | |
| PlaceContext::MutatingUse(MutatingUseContext::Drop), | |
| location | |
| ); | |
| } | |
| TerminatorKind::Call { | |
| func, | |
| args, | |
| destination, | |
| target: _, | |
| unwind: _, | |
| call_source: _, | |
| fn_span, | |
| } => { | |
| self.visit_span($(& $mutability)? *fn_span); | |
| self.visit_operand(func, location); | |
| for arg in args { | |
| self.visit_operand(&$($mutability)? arg.node, location); | |
| } | |
| self.visit_place( | |
| destination, | |
| PlaceContext::MutatingUse(MutatingUseContext::Call), | |
| location | |
| ); | |
| } | |
| TerminatorKind::TailCall { func, args, fn_span } => { | |
| self.visit_span($(& $mutability)? *fn_span); | |
| self.visit_operand(func, location); | |
| for arg in args { | |
| self.visit_operand(&$($mutability)? arg.node, location); | |
| } | |
| }, | |
| TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { | |
| 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 { | |
| asm_macro: _, | |
| template: _, | |
| operands, | |
| options: _, | |
| line_spans: _, | |
| targets: _, | |
| unwind: _, | |
| } => { | |
| 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_const_operand(value, location); | |
| } | |
| InlineAsmOperand::Out { place: None, .. } | |
| | InlineAsmOperand::SymStatic { def_id: _ } | |
| | InlineAsmOperand::Label { target_index: _ } => {} | |
| } | |
| } | |
| } | |
| } | |
| } | |
| 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) | InvalidEnumConstruction(op) => { | |
| self.visit_operand(op, location); | |
| } | |
| ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference | NullReferenceConstructed | ResumedAfterDrop(_) => { | |
| // Nothing to visit | |
| } | |
| MisalignedPointerDereference { required, found } => { | |
| self.visit_operand(required, location); | |
| self.visit_operand(found, location); | |
| } | |
| } | |
| } | |
| fn super_rvalue( | |
| &mut self, | |
| rvalue: & $($mutability)? Rvalue<'tcx>, | |
| location: Location | |
| ) { | |
| match rvalue { | |
| Rvalue::Use(operand, _with_retag) => { | |
| self.visit_operand(operand, location); | |
| } | |
| Rvalue::Repeat(value, ct) => { | |
| self.visit_operand(value, location); | |
| self.visit_ty_const($(&$mutability)? *ct, 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::Fake(_) => PlaceContext::NonMutatingUse( | |
| NonMutatingUseContext::FakeBorrow | |
| ), | |
| BorrowKind::Mut { .. } => | |
| PlaceContext::MutatingUse(MutatingUseContext::Borrow), | |
| }; | |
| self.visit_place(path, ctx, location); | |
| } | |
| Rvalue::Reborrow(target, mutability, place) => { | |
| self.visit_ty($(& $mutability)? *target, TyContext::Location(location)); | |
| self.visit_place( | |
| place, | |
| match mutability { | |
| Mutability::Not => PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow), | |
| Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::Borrow), | |
| }, | |
| location | |
| ); | |
| } | |
| Rvalue::CopyForDeref(place) => { | |
| self.visit_place( | |
| place, | |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), | |
| location | |
| ); | |
| } | |
| Rvalue::RawPtr(m, path) => { | |
| let ctx = match m { | |
| RawPtrKind::Mut => PlaceContext::MutatingUse( | |
| MutatingUseContext::RawBorrow | |
| ), | |
| RawPtrKind::Const => PlaceContext::NonMutatingUse( | |
| NonMutatingUseContext::RawBorrow | |
| ), | |
| RawPtrKind::FakeForPtrMetadata => PlaceContext::NonMutatingUse( | |
| NonMutatingUseContext::Inspect | |
| ), | |
| }; | |
| self.visit_place(path, ctx, location); | |
| } | |
| Rvalue::Cast(_cast_kind, operand, ty) => { | |
| self.visit_operand(operand, location); | |
| self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); | |
| } | |
| Rvalue::BinaryOp(_bin_op, (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::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, | |
| args, | |
| _user_args, | |
| _active_field_index | |
| ) => { | |
| self.visit_args(args, location); | |
| } | |
| AggregateKind::Closure(_, closure_args) => { | |
| self.visit_args(closure_args, location); | |
| } | |
| AggregateKind::Coroutine(_, coroutine_args) => { | |
| self.visit_args(coroutine_args, location); | |
| } | |
| AggregateKind::CoroutineClosure(_, coroutine_closure_args) => { | |
| self.visit_args(coroutine_closure_args, location); | |
| } | |
| AggregateKind::RawPtr(ty, _) => { | |
| self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); | |
| } | |
| } | |
| for operand in operands { | |
| self.visit_operand(operand, location); | |
| } | |
| } | |
| Rvalue::WrapUnsafeBinder(op, ty) => { | |
| self.visit_operand(op, 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_const_operand(constant, location); | |
| } | |
| Operand::RuntimeChecks(_) => {} | |
| } | |
| } | |
| 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($(* &$mutability *)? variance) | |
| ), | |
| location | |
| ); | |
| self.visit_user_type_projection(user_ty); | |
| } | |
| fn super_coverage( | |
| &mut self, | |
| _kind: & $($mutability)? coverage::CoverageKind, | |
| _location: Location | |
| ) { | |
| } | |
| fn super_local_decl( | |
| &mut self, | |
| local: Local, | |
| local_decl: & $($mutability)? LocalDecl<'tcx> | |
| ) { | |
| let LocalDecl { | |
| mutability: _, | |
| ty, | |
| user_ty, | |
| source_info, | |
| local_info: _, | |
| } = local_decl; | |
| self.visit_source_info(source_info); | |
| 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); | |
| } | |
| } | |
| } | |
| fn super_local( | |
| &mut self, | |
| _local: $(& $mutability)? Local, | |
| _context: PlaceContext, | |
| _location: Location, | |
| ) { | |
| } | |
| fn super_var_debug_info( | |
| &mut self, | |
| var_debug_info: & $($mutability)? VarDebugInfo<'tcx> | |
| ) { | |
| let VarDebugInfo { | |
| name: _, | |
| source_info, | |
| composite, | |
| value, | |
| argument_index: _, | |
| } = var_debug_info; | |
| self.visit_source_info(source_info); | |
| let location = Location::START; | |
| if let Some(VarDebugInfoFragment { | |
| ty, | |
| projection | |
| }) = composite { | |
| self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); | |
| for elem in projection { | |
| let ProjectionElem::Field(_, ty) = elem else { bug!() }; | |
| self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); | |
| } | |
| } | |
| match value { | |
| VarDebugInfoContents::Const(c) => self.visit_const_operand(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_const_operand( | |
| &mut self, | |
| constant: & $($mutability)? ConstOperand<'tcx>, | |
| location: Location | |
| ) { | |
| let ConstOperand { | |
| span, | |
| user_ty: _, // no visit method for this | |
| const_, | |
| } = constant; | |
| self.visit_span($(& $mutability)? *span); | |
| match const_ { | |
| Const::Ty(_, ct) => self.visit_ty_const($(&$mutability)? *ct, location), | |
| Const::Val(_, ty) | Const::Unevaluated(_, ty) => { | |
| self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); | |
| } | |
| } | |
| } | |
| fn super_ty_const( | |
| &mut self, | |
| _ct: $(& $mutability)? ty::Const<'tcx>, | |
| _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_args(&mut self, _args: & $($mutability)? GenericArgsRef<'tcx>) {} | |
| // Convenience methods | |
| fn visit_location( | |
| &mut self, | |
| body: &$($mutability)? Body<'tcx>, | |
| location: Location | |
| ) { | |
| let basic_block = | |
| & $($mutability)? basic_blocks!(body, $($mutability, true)?)[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! basic_blocks { | |
| ($body:ident, mut, true) => { | |
| $body.basic_blocks.as_mut() | |
| }; | |
| ($body:ident, mut, false) => { | |
| $body.basic_blocks.as_mut_preserves_cfg() | |
| }; | |
| ($body:ident,) => { | |
| $body.basic_blocks | |
| }; | |
| } | |
| macro_rules! basic_blocks_iter { | |
| ($body:ident, mut, $invalidate:tt) => { | |
| basic_blocks!($body, mut, $invalidate).iter_enumerated_mut() | |
| }; | |
| ($body:ident,) => { | |
| basic_blocks!($body,).iter_enumerated() | |
| }; | |
| } | |
| macro_rules! extra_body_methods { | |
| (mut) => { | |
| fn visit_body_preserves_cfg(&mut self, body: &mut Body<'tcx>) { | |
| self.super_body_preserves_cfg(body); | |
| } | |
| fn super_body_preserves_cfg(&mut self, body: &mut Body<'tcx>) { | |
| super_body!(self, body, mut, false); | |
| } | |
| }; | |
| () => {}; | |
| } | |
| macro_rules! super_body { | |
| ($self:ident, $body:ident, $($mutability:ident, $invalidate:tt)?) => { | |
| let span = $body.span; | |
| if let Some(coroutine) = &$($mutability)? $body.coroutine { | |
| if let Some(yield_ty) = $(& $mutability)? coroutine.yield_ty { | |
| $self.visit_ty( | |
| yield_ty, | |
| TyContext::YieldTy(SourceInfo::outermost(span)) | |
| ); | |
| } | |
| if let Some(resume_ty) = $(& $mutability)? coroutine.resume_ty { | |
| $self.visit_ty( | |
| resume_ty, | |
| TyContext::ResumeTy(SourceInfo::outermost(span)) | |
| ); | |
| } | |
| } | |
| for var_debug_info in &$($mutability)? $body.var_debug_info { | |
| $self.visit_var_debug_info(var_debug_info); | |
| } | |
| for (bb, data) in basic_blocks_iter!($body, $($mutability, $invalidate)?) { | |
| $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]); | |
| } | |
| 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 | |
| ); | |
| } | |
| $self.visit_span($(& $mutability)? $body.span); | |
| if let Some(required_consts) = &$($mutability)? $body.required_consts { | |
| for const_ in required_consts { | |
| let location = Location::START; | |
| $self.visit_const_operand(const_, 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().mk_place_elems(&new_projection); | |
| } | |
| } | |
| fn process_projection<'a>( | |
| &mut self, | |
| projection: &'a [PlaceElem<'tcx>], | |
| location: Location, | |
| ) -> Option<Vec<PlaceElem<'tcx>>> { | |
| 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<PlaceElem<'tcx>> { | |
| 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::OpaqueCast(ty) => { | |
| let mut new_ty = ty; | |
| self.visit_ty(&mut new_ty, TyContext::Location(location)); | |
| if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None } | |
| } | |
| PlaceElem::UnwrapUnsafeBinder(ty) => { | |
| let mut new_ty = ty; | |
| self.visit_ty(&mut new_ty, TyContext::Location(location)); | |
| if ty != new_ty { Some(PlaceElem::UnwrapUnsafeBinder(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, | |
| place_ref: PlaceRef<'tcx>, | |
| elem: PlaceElem<'tcx>, | |
| context: PlaceContext, | |
| location: Location, | |
| ) { | |
| self.super_projection_elem(place_ref, elem, context, location); | |
| } | |
| fn super_place( | |
| &mut self, | |
| place: &Place<'tcx>, | |
| mut context: PlaceContext, | |
| location: Location, | |
| ) { | |
| if !place.projection.is_empty() && 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() { | |
| self.visit_projection_elem(base, elem, context, location); | |
| } | |
| } | |
| fn super_projection_elem( | |
| &mut self, | |
| _place_ref: PlaceRef<'tcx>, | |
| elem: PlaceElem<'tcx>, | |
| context: PlaceContext, | |
| location: Location, | |
| ) { | |
| match elem { | |
| ProjectionElem::OpaqueCast(ty) | |
| | ProjectionElem::Field(_, ty) | |
| | ProjectionElem::UnwrapUnsafeBinder(ty) => { | |
| self.visit_ty(ty, TyContext::Location(location)); | |
| } | |
| ProjectionElem::Index(local) => { | |
| self.visit_local( | |
| local, | |
| if context.is_use() { | |
| // ^ Only change the context if it is a real use, not a "use" in debuginfo. | |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) | |
| } else { | |
| context | |
| }, | |
| 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); | |
| /// Extra information passed to `visit_ty` and friends to give context | |
| /// about where the type etc appears. | |
| #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] | |
| 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), | |
| ResumeTy(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, | |
| /// A fake borrow. | |
| /// FIXME: do we need to distinguish shallow and deep fake borrows? In fact, do we need to | |
| /// distinguish fake and normal deep borrows? | |
| FakeBorrow, | |
| /// `&raw const`. | |
| RawBorrow, | |
| /// PlaceMention statement. | |
| /// | |
| /// This statement is executed as a check that the `Place` is live without reading from it, | |
| /// so it must be considered as a non-mutating use. | |
| PlaceMention, | |
| /// 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, | |
| /// Output operand of an inline assembly block. | |
| AsmOutput, | |
| /// Destination of a call. | |
| Call, | |
| /// Destination of a yield. | |
| Yield, | |
| /// Being dropped. | |
| Drop, | |
| /// Mutable borrow. | |
| Borrow, | |
| /// `&raw mut`. | |
| RawBorrow, | |
| /// 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(ty::Variance), | |
| /// The data of a user variable, for debug info. | |
| VarDebugInfo, | |
| /// A `BackwardIncompatibleDropHint` statement, meant for edition 2024 lints. | |
| BackwardIncompatibleDropHint, | |
| } | |
| #[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, excluding fake borrows | |
| /// (which are an artifact of borrowck and not actually borrows in runtime MIR). | |
| pub fn is_borrow(self) -> bool { | |
| matches!( | |
| self, | |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) | |
| | PlaceContext::MutatingUse(MutatingUseContext::Borrow) | |
| ) | |
| } | |
| /// Returns `true` if this place context represents an address-of. | |
| pub fn is_address_of(self) -> bool { | |
| matches!( | |
| self, | |
| PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow) | |
| | PlaceContext::MutatingUse(MutatingUseContext::RawBorrow) | |
| ) | |
| } | |
| /// Returns `true` if this place context may be used to know the address of the given place. | |
| #[inline] | |
| pub fn may_observe_address(self) -> bool { | |
| matches!( | |
| self, | |
| PlaceContext::NonMutatingUse( | |
| NonMutatingUseContext::SharedBorrow | |
| | NonMutatingUseContext::RawBorrow | |
| | NonMutatingUseContext::FakeBorrow | |
| ) | PlaceContext::MutatingUse( | |
| MutatingUseContext::Drop | |
| | MutatingUseContext::Borrow | |
| | MutatingUseContext::RawBorrow | |
| | MutatingUseContext::AsmOutput | |
| ) | |
| ) | |
| } | |
| /// 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, | |
| ) | |
| ) | |
| } | |
| /// The variance of a place in the given context. | |
| pub fn ambient_variance(self) -> ty::Variance { | |
| use NonMutatingUseContext::*; | |
| use NonUseContext::*; | |
| match self { | |
| PlaceContext::MutatingUse(_) => ty::Invariant, | |
| PlaceContext::NonUse( | |
| StorageDead | StorageLive | VarDebugInfo | BackwardIncompatibleDropHint, | |
| ) => ty::Invariant, | |
| PlaceContext::NonMutatingUse( | |
| Inspect | Copy | Move | PlaceMention | SharedBorrow | FakeBorrow | RawBorrow | |
| | Projection, | |
| ) => ty::Covariant, | |
| PlaceContext::NonUse(AscribeUserTy(variance)) => variance, | |
| } | |
| } | |
| } | |
| /// Small utility to visit places and locals without manually implementing a full visitor. | |
| pub struct VisitPlacesWith<F>(pub F); | |
| impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith<F> | |
| where | |
| F: FnMut(Place<'tcx>, PlaceContext), | |
| { | |
| fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) { | |
| (self.0)(local.into(), ctxt); | |
| } | |
| fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) { | |
| (self.0)(*place, ctxt); | |
| self.visit_projection(place.as_ref(), ctxt, location); | |
| } | |
| } |