// 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use middle::def_id::DefId; use middle::ty::Region; use mir::repr::*; use rustc_data_structures::tuple_slice::TupleSlice; use syntax::codemap::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_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_statement(&mut self, block: BasicBlock, statement: & $($mutability)* Statement<'tcx>) { self.super_statement(block, statement); } fn visit_assign(&mut self, block: BasicBlock, lvalue: & $($mutability)* Lvalue<'tcx>, rvalue: & $($mutability)* Rvalue<'tcx>) { self.super_assign(block, lvalue, rvalue); } fn visit_terminator(&mut self, block: BasicBlock, terminator: & $($mutability)* Terminator<'tcx>) { self.super_terminator(block, terminator); } fn visit_rvalue(&mut self, rvalue: & $($mutability)* Rvalue<'tcx>) { self.super_rvalue(rvalue); } fn visit_operand(&mut self, operand: & $($mutability)* Operand<'tcx>) { self.super_operand(operand); } fn visit_lvalue(&mut self, lvalue: & $($mutability)* Lvalue<'tcx>, context: LvalueContext) { self.super_lvalue(lvalue, context); } fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) { self.super_branch(source, target); } fn visit_constant(&mut self, constant: & $($mutability)* Constant<'tcx>) { self.super_constant(constant); } fn visit_literal(&mut self, literal: & $($mutability)* Literal<'tcx>) { self.super_literal(literal); } fn visit_def_id(&mut self, def_id: & $($mutability)* DefId) { self.super_def_id(def_id); } fn visit_span(&mut self, span: & $($mutability)* Span) { self.super_span(span); } // The `super_xxx` methods comprise the default behavior and are // not meant to be overidden. fn super_mir(&mut self, mir: & $($mutability)* Mir<'tcx>) { for block in mir.all_basic_blocks() { let data = & $($mutability)* mir[block]; self.visit_basic_block_data(block, data); } } fn super_basic_block_data(&mut self, block: BasicBlock, data: & $($mutability)* BasicBlockData<'tcx>) { for statement in & $($mutability)* data.statements { self.visit_statement(block, statement); } if let Some(ref $($mutability)* terminator) = data.terminator { self.visit_terminator(block, terminator); } } fn super_statement(&mut self, block: BasicBlock, statement: & $($mutability)* Statement<'tcx>) { self.visit_span(& $($mutability)* statement.span); match statement.kind { StatementKind::Assign(ref $($mutability)* lvalue, ref $($mutability)* rvalue) => { self.visit_assign(block, lvalue, rvalue); } StatementKind::Drop(_, ref $($mutability)* lvalue) => { self.visit_lvalue(lvalue, LvalueContext::Drop); } } } fn super_assign(&mut self, _block: BasicBlock, lvalue: &$($mutability)* Lvalue<'tcx>, rvalue: &$($mutability)* Rvalue<'tcx>) { self.visit_lvalue(lvalue, LvalueContext::Store); self.visit_rvalue(rvalue); } fn super_terminator(&mut self, block: BasicBlock, terminator: &$($mutability)* Terminator<'tcx>) { match *terminator { Terminator::Goto { target } => { self.visit_branch(block, target); } Terminator::If { ref $($mutability)* cond, ref $($mutability)* targets } => { self.visit_operand(cond); for &target in targets.as_slice() { self.visit_branch(block, target); } } Terminator::Switch { ref $($mutability)* discr, adt_def: _, ref targets } => { self.visit_lvalue(discr, LvalueContext::Inspect); for &target in targets { self.visit_branch(block, target); } } Terminator::SwitchInt { ref $($mutability)* discr, switch_ty: _, values: _, ref targets } => { self.visit_lvalue(discr, LvalueContext::Inspect); for &target in targets { self.visit_branch(block, target); } } Terminator::Resume | Terminator::Return => { } Terminator::Call { ref $($mutability)* func, ref $($mutability)* args, ref $($mutability)* kind } => { self.visit_operand(func); for arg in args { self.visit_operand(arg); } match *kind { CallKind::Converging { ref $($mutability)* destination, .. } | CallKind::ConvergingCleanup { ref $($mutability)* destination, .. } => { self.visit_lvalue(destination, LvalueContext::Store); } CallKind::Diverging | CallKind::DivergingCleanup(_) => {} } for &target in kind.successors() { self.visit_branch(block, target); } } } } fn super_rvalue(&mut self, rvalue: & $($mutability)* Rvalue<'tcx>) { match *rvalue { Rvalue::Use(ref $($mutability)* operand) => { self.visit_operand(operand); } Rvalue::Repeat(ref $($mutability)* value, ref $($mutability)* len) => { self.visit_operand(value); self.visit_constant(len); } Rvalue::Ref(r, bk, ref $($mutability)* path) => { self.visit_lvalue(path, LvalueContext::Borrow { region: r, kind: bk }); } Rvalue::Len(ref $($mutability)* path) => { self.visit_lvalue(path, LvalueContext::Inspect); } Rvalue::Cast(_, ref $($mutability)* operand, _) => { self.visit_operand(operand); } Rvalue::BinaryOp(_, ref $($mutability)* lhs, ref $($mutability)* rhs) => { self.visit_operand(lhs); self.visit_operand(rhs); } Rvalue::UnaryOp(_, ref $($mutability)* op) => { self.visit_operand(op); } Rvalue::Box(_) => { } Rvalue::Aggregate(ref $($mutability)* kind, ref $($mutability)* operands) => { match *kind { AggregateKind::Closure(ref $($mutability)* def_id, _) => { self.visit_def_id(def_id); } _ => { /* nothing to do */ } } for operand in & $($mutability)* operands[..] { self.visit_operand(operand); } } Rvalue::Slice { ref $($mutability)* input, from_start, from_end } => { self.visit_lvalue(input, LvalueContext::Slice { from_start: from_start, from_end: from_end, }); } Rvalue::InlineAsm(_) => { } } } fn super_operand(&mut self, operand: & $($mutability)* Operand<'tcx>) { match *operand { Operand::Consume(ref $($mutability)* lvalue) => { self.visit_lvalue(lvalue, LvalueContext::Consume); } Operand::Constant(ref $($mutability)* constant) => { self.visit_constant(constant); } } } fn super_lvalue(&mut self, lvalue: & $($mutability)* Lvalue<'tcx>, _context: LvalueContext) { match *lvalue { Lvalue::Var(_) | Lvalue::Temp(_) | Lvalue::Arg(_) | Lvalue::ReturnPointer => { } Lvalue::Static(ref $($mutability)* def_id) => { self.visit_def_id(def_id); } Lvalue::Projection(ref $($mutability)* proj) => { self.visit_lvalue(& $($mutability)* proj.base, LvalueContext::Projection); } } } fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) { } fn super_constant(&mut self, constant: & $($mutability)* Constant<'tcx>) { self.visit_span(& $($mutability)* constant.span); self.visit_literal(& $($mutability)* constant.literal); } fn super_literal(&mut self, literal: & $($mutability)* Literal<'tcx>) { match *literal { Literal::Item { ref $($mutability)* def_id, .. } => { self.visit_def_id(def_id); }, Literal::Value { .. } => { // Nothing to do } } } fn super_def_id(&mut self, _def_id: & $($mutability)* DefId) { } fn super_span(&mut self, _span: & $($mutability)* Span) { } } } } make_mir_visitor!(Visitor,); make_mir_visitor!(MutVisitor,mut); #[derive(Copy, Clone, Debug)] pub enum LvalueContext { // Appears as LHS of an assignment or as dest of a call Store, // Being dropped Drop, // Being inspected in some way, like loading a len Inspect, // Being borrowed Borrow { region: Region, kind: BorrowKind }, // Being sliced -- this should be same as being borrowed, probably Slice { from_start: usize, from_end: usize }, // Used as base for another lvalue, e.g. `x` in `x.y` Projection, // Consumed as part of an operand Consume, }