From ed7c30b8885afb7e0bf26072dcb7e39658baade0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 24 Mar 2016 06:12:19 -0400 Subject: [PATCH] rework MIR visitor We now visit more things (e.g., types) and also follow a deliberate style designed to reduce omissions. --- src/librustc/mir/repr.rs | 15 ++ src/librustc/mir/visit.rs | 393 +++++++++++++++++++++++++++++++++++--- 2 files changed, 382 insertions(+), 26 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 4022d762aec3..47d923cbce35 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -233,8 +233,23 @@ impl Debug for BasicBlock { #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct BasicBlockData<'tcx> { + /// List of statements in this block. pub statements: Vec>, + + /// Terminator for this block. + /// + /// NB. This should generally ONLY be `None` during construction. + /// Therefore, you should generally access it via the + /// `terminator()` or `terminator_mut()` methods. The only + /// exception is that certain passes, such as `simplify_cfg`, swap + /// out the terminator temporarily with `None` while they continue + /// to recurse over the set of basic blocks. pub terminator: Option>, + + /// If true, this block lies on an unwind path. This is used + /// during trans where distinct kinds of basic blocks may be + /// generated (particularly for MSVC cleanup). Unwind blocks must + /// only branch to other unwind blocks. pub is_cleanup: bool, } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index c7e4b825d58a..5be2e0684147 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -8,12 +8,79 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use middle::const_eval::ConstVal; use middle::def_id::DefId; -use middle::ty::Region; +use middle::subst::Substs; +use middle::ty::{ClosureSubsts, FnOutput, Region, Ty}; use mir::repr::*; +use rustc_const_eval::ConstUsize; use rustc_data_structures::tuple_slice::TupleSlice; use syntax::codemap::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`. But +// there is one exception: we do destructure the `FnOutput` to reach +// the type within. Just because. +// +// ## 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 { } = *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> { @@ -30,6 +97,11 @@ macro_rules! make_mir_visitor { self.super_basic_block_data(block, data); } + fn visit_scope_data(&mut self, + scope_data: & $($mutability)* ScopeData) { + self.super_scope_data(scope_data); + } + fn visit_statement(&mut self, block: BasicBlock, statement: & $($mutability)* Statement<'tcx>) { @@ -49,6 +121,12 @@ macro_rules! make_mir_visitor { self.super_terminator(block, terminator); } + fn visit_terminator_kind(&mut self, + block: BasicBlock, + kind: & $($mutability)* TerminatorKind<'tcx>) { + self.super_terminator_kind(block, kind); + } + fn visit_rvalue(&mut self, rvalue: & $($mutability)* Rvalue<'tcx>) { self.super_rvalue(rvalue); @@ -65,6 +143,18 @@ macro_rules! make_mir_visitor { self.super_lvalue(lvalue, context); } + fn visit_projection(&mut self, + lvalue: & $($mutability)* LvalueProjection<'tcx>, + context: LvalueContext) { + self.super_projection(lvalue, context); + } + + fn visit_projection_elem(&mut self, + lvalue: & $($mutability)* LvalueElem<'tcx>, + context: LvalueContext) { + self.super_projection_elem(lvalue, context); + } + fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) { @@ -91,35 +181,143 @@ macro_rules! make_mir_visitor { self.super_span(span); } + fn visit_fn_output(&mut self, + fn_output: & $($mutability)* FnOutput<'tcx>) { + self.super_fn_output(fn_output); + } + + fn visit_ty(&mut self, + ty: & $($mutability)* Ty<'tcx>) { + self.super_ty(ty); + } + + fn visit_substs(&mut self, + substs: & $($mutability)* &'tcx Substs<'tcx>) { + self.super_substs(substs); + } + + fn visit_closure_substs(&mut self, + substs: & $($mutability)* &'tcx ClosureSubsts<'tcx>) { + self.super_closure_substs(substs); + } + + fn visit_const_val(&mut self, + const_val: & $($mutability)* ConstVal) { + self.super_const_val(const_val); + } + + fn visit_const_usize(&mut self, + const_usize: & $($mutability)* ConstUsize) { + self.super_const_usize(const_usize); + } + + fn visit_typed_const_val(&mut self, + val: & $($mutability)* TypedConstVal<'tcx>) { + self.super_typed_const_val(val); + } + + fn visit_var_decl(&mut self, + var_decl: & $($mutability)* VarDecl<'tcx>) { + self.super_var_decl(var_decl); + } + + fn visit_temp_decl(&mut self, + temp_decl: & $($mutability)* TempDecl<'tcx>) { + self.super_temp_decl(temp_decl); + } + + fn visit_arg_decl(&mut self, + arg_decl: & $($mutability)* ArgDecl<'tcx>) { + self.super_arg_decl(arg_decl); + } + + fn visit_scope_id(&mut self, + scope_id: & $($mutability)* ScopeId) { + self.super_scope_id(scope_id); + } + // The `super_xxx` methods comprise the default behavior and are // not meant to be overridden. fn super_mir(&mut self, mir: & $($mutability)* Mir<'tcx>) { - for block in mir.all_basic_blocks() { - let data = & $($mutability)* mir[block]; + let Mir { + ref $($mutability)* basic_blocks, + ref $($mutability)* scopes, + ref $($mutability)* return_ty, + ref $($mutability)* var_decls, + ref $($mutability)* arg_decls, + ref $($mutability)* temp_decls, + ref $($mutability)* span, + } = *mir; + + for (index, data) in basic_blocks.into_iter().enumerate() { + let block = BasicBlock::new(index); self.visit_basic_block_data(block, data); } + + for scope in scopes { + self.visit_scope_data(scope); + } + + self.visit_fn_output(return_ty); + + for var_decl in var_decls { + self.visit_var_decl(var_decl); + } + + for arg_decl in arg_decls { + self.visit_arg_decl(arg_decl); + } + + for temp_decl in temp_decls { + self.visit_temp_decl(temp_decl); + } + + self.visit_span(span); } fn super_basic_block_data(&mut self, block: BasicBlock, data: & $($mutability)* BasicBlockData<'tcx>) { - for statement in & $($mutability)* data.statements { + let BasicBlockData { + ref $($mutability)* statements, + ref $($mutability)* terminator, + is_cleanup: _ + } = *data; + + for statement in statements { self.visit_statement(block, statement); } - if let Some(ref $($mutability)* terminator) = data.terminator { + if let Some(ref $($mutability)* terminator) = *terminator { self.visit_terminator(block, terminator); } } + fn super_scope_data(&mut self, + scope_data: & $($mutability)* ScopeData) { + let ScopeData { + ref $($mutability)* parent_scope, + } = *scope_data; + + if let Some(ref $($mutability)* parent_scope) = *parent_scope { + self.visit_scope_id(parent_scope); + } + } + fn super_statement(&mut self, block: BasicBlock, statement: & $($mutability)* Statement<'tcx>) { - self.visit_span(& $($mutability)* statement.span); + let Statement { + ref $($mutability)* span, + ref $($mutability)* scope, + ref $($mutability)* kind, + } = *statement; - match statement.kind { + self.visit_span(span); + self.visit_scope_id(scope); + match *kind { StatementKind::Assign(ref $($mutability)* lvalue, ref $($mutability)* rvalue) => { self.visit_assign(block, lvalue, rvalue); @@ -138,7 +336,21 @@ macro_rules! make_mir_visitor { fn super_terminator(&mut self, block: BasicBlock, terminator: &$($mutability)* Terminator<'tcx>) { - match terminator.kind { + let Terminator { + ref $($mutability)* span, + ref $($mutability)* scope, + ref $($mutability)* kind, + } = *terminator; + + self.visit_span(span); + self.visit_scope_id(scope); + self.visit_terminator_kind(block, kind); + } + + fn super_terminator_kind(&mut self, + block: BasicBlock, + kind: & $($mutability)* TerminatorKind<'tcx>) { + match *kind { TerminatorKind::Goto { target } => { self.visit_branch(block, target); } @@ -161,10 +373,14 @@ macro_rules! make_mir_visitor { } TerminatorKind::SwitchInt { ref $($mutability)* discr, - switch_ty: _, - values: _, + ref $($mutability)* switch_ty, + ref $($mutability)* values, ref targets } => { self.visit_lvalue(discr, LvalueContext::Inspect); + self.visit_ty(switch_ty); + for value in values { + self.visit_const_val(value); + } for &target in targets { self.visit_branch(block, target); } @@ -207,8 +423,9 @@ macro_rules! make_mir_visitor { } Rvalue::Repeat(ref $($mutability)* value, - _) => { + ref $($mutability)* typed_const_val) => { self.visit_operand(value); + self.visit_typed_const_val(typed_const_val); } Rvalue::Ref(r, bk, ref $($mutability)* path) => { @@ -222,34 +439,48 @@ macro_rules! make_mir_visitor { self.visit_lvalue(path, LvalueContext::Inspect); } - Rvalue::Cast(_, ref $($mutability)* operand, _) => { + Rvalue::Cast(_cast_kind, + ref $($mutability)* operand, + ref $($mutability)* ty) => { self.visit_operand(operand); + self.visit_ty(ty); } - Rvalue::BinaryOp(_, + Rvalue::BinaryOp(_bin_op, ref $($mutability)* lhs, ref $($mutability)* rhs) => { self.visit_operand(lhs); self.visit_operand(rhs); } - Rvalue::UnaryOp(_, ref $($mutability)* op) => { + Rvalue::UnaryOp(_un_op, ref $($mutability)* op) => { self.visit_operand(op); } - Rvalue::Box(_) => { + Rvalue::Box(ref $($mutability)* ty) => { + self.visit_ty(ty); } Rvalue::Aggregate(ref $($mutability)* kind, ref $($mutability)* operands) => { match *kind { - AggregateKind::Closure(ref $($mutability)* def_id, _) => { - self.visit_def_id(def_id); + AggregateKind::Vec => { + } + AggregateKind::Tuple => { + } + AggregateKind::Adt(_adt_def, + _variant_index, + ref $($mutability)* substs) => { + self.visit_substs(substs); + } + AggregateKind::Closure(ref $($mutability)* def_id, + ref $($mutability)* closure_substs) => { + self.visit_def_id(def_id); + self.visit_closure_substs(closure_substs); } - _ => { /* nothing to do */ } } - for operand in & $($mutability)* operands[..] { + for operand in operands { self.visit_operand(operand); } } @@ -264,7 +495,8 @@ macro_rules! make_mir_visitor { } Rvalue::InlineAsm { ref $($mutability)* outputs, - ref $($mutability)* inputs, .. } => { + ref $($mutability)* inputs, + asm: _ } => { for output in & $($mutability)* outputs[..] { self.visit_lvalue(output, LvalueContext::Store); } @@ -289,7 +521,7 @@ macro_rules! make_mir_visitor { fn super_lvalue(&mut self, lvalue: & $($mutability)* Lvalue<'tcx>, - _context: LvalueContext) { + context: LvalueContext) { match *lvalue { Lvalue::Var(_) | Lvalue::Temp(_) | @@ -300,12 +532,81 @@ macro_rules! make_mir_visitor { self.visit_def_id(def_id); } Lvalue::Projection(ref $($mutability)* proj) => { - self.visit_lvalue(& $($mutability)* proj.base, - LvalueContext::Projection); + self.visit_projection(proj, context); } } } + fn super_projection(&mut self, + proj: & $($mutability)* LvalueProjection<'tcx>, + context: LvalueContext) { + let Projection { + ref $($mutability)* base, + ref $($mutability)* elem, + } = *proj; + self.visit_lvalue(base, LvalueContext::Projection); + self.visit_projection_elem(elem, context); + } + + fn super_projection_elem(&mut self, + proj: & $($mutability)* LvalueElem<'tcx>, + _context: LvalueContext) { + match *proj { + ProjectionElem::Deref => { + } + ProjectionElem::Field(_field, ref $($mutability)* ty) => { + self.visit_ty(ty); + } + ProjectionElem::Index(ref $($mutability)* operand) => { + self.visit_operand(operand); + } + ProjectionElem::ConstantIndex { offset: _, + min_length: _, + from_end: _ } => { + } + ProjectionElem::Downcast(_adt_def, _variant_index) => { + } + } + } + + fn super_var_decl(&mut self, + var_decl: & $($mutability)* VarDecl<'tcx>) { + let VarDecl { + mutability: _, + name: _, + ref $($mutability)* ty, + ref $($mutability)* scope, + ref $($mutability)* span, + } = *var_decl; + + self.visit_ty(ty); + self.visit_scope_id(scope); + self.visit_span(span); + } + + fn super_temp_decl(&mut self, + temp_decl: & $($mutability)* TempDecl<'tcx>) { + let TempDecl { + ref $($mutability)* ty, + } = *temp_decl; + + self.visit_ty(ty); + } + + fn super_arg_decl(&mut self, + arg_decl: & $($mutability)* ArgDecl<'tcx>) { + let ArgDecl { + ref $($mutability)* ty, + spread: _ + } = *arg_decl; + + self.visit_ty(ty); + } + + fn super_scope_id(&mut self, + _scope_id: & $($mutability)* ScopeId) { + } + fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) { @@ -314,17 +615,32 @@ macro_rules! make_mir_visitor { fn super_constant(&mut self, constant: & $($mutability)* Constant<'tcx>) { self.visit_span(& $($mutability)* constant.span); + self.visit_ty(& $($mutability)* constant.ty); self.visit_literal(& $($mutability)* constant.literal); } + fn super_typed_const_val(&mut self, + constant: & $($mutability)* TypedConstVal<'tcx>) { + let TypedConstVal { + ref $($mutability)* span, + ref $($mutability)* ty, + ref $($mutability)* value, + } = *constant; + self.visit_span(span); + self.visit_ty(ty); + self.visit_const_usize(value); + } + fn super_literal(&mut self, literal: & $($mutability)* Literal<'tcx>) { match *literal { - Literal::Item { ref $($mutability)* def_id, .. } => { + Literal::Item { ref $($mutability)* def_id, + ref $($mutability)* substs } => { self.visit_def_id(def_id); + self.visit_substs(substs); }, - Literal::Value { .. } => { - // Nothing to do + Literal::Value { ref $($mutability)* value } => { + self.visit_const_val(value); } } } @@ -334,6 +650,31 @@ macro_rules! make_mir_visitor { fn super_span(&mut self, _span: & $($mutability)* Span) { } + + fn super_fn_output(&mut self, fn_output: & $($mutability)* FnOutput<'tcx>) { + match *fn_output { + FnOutput::FnConverging(ref $($mutability)* ty) => { + self.visit_ty(ty); + } + FnOutput::FnDiverging => { + } + } + } + + fn super_ty(&mut self, _ty: & $($mutability)* Ty<'tcx>) { + } + + fn super_substs(&mut self, _substs: & $($mutability)* &'tcx Substs<'tcx>) { + } + + fn super_closure_substs(&mut self, _substs: & $($mutability)* &'tcx ClosureSubsts<'tcx>) { + } + + fn super_const_val(&mut self, _substs: & $($mutability)* ConstVal) { + } + + fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) { + } } } }