]> git.lizzy.rs Git - rust.git/commitdiff
move the drop expansion code to rustc_mir
authorAriel Ben-Yehuda <ariel.byd@gmail.com>
Thu, 9 Mar 2017 18:10:05 +0000 (20:10 +0200)
committerAriel Ben-Yehuda <ariel.byd@gmail.com>
Sat, 18 Mar 2017 00:53:07 +0000 (02:53 +0200)
19 files changed:
src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs
src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
src/librustc_borrowck/borrowck/mir/mod.rs
src/librustc_borrowck/borrowck/mir/patch.rs [deleted file]
src/librustc_driver/pretty.rs
src/librustc_mir/def_use.rs [deleted file]
src/librustc_mir/graphviz.rs [deleted file]
src/librustc_mir/lib.rs
src/librustc_mir/mir_map.rs
src/librustc_mir/pretty.rs [deleted file]
src/librustc_mir/transform/copy_prop.rs
src/librustc_mir/transform/dump_mir.rs
src/librustc_mir/util/def_use.rs [new file with mode: 0644]
src/librustc_mir/util/elaborate_drops.rs [new file with mode: 0644]
src/librustc_mir/util/graphviz.rs [new file with mode: 0644]
src/librustc_mir/util/mod.rs [new file with mode: 0644]
src/librustc_mir/util/patch.rs [new file with mode: 0644]
src/librustc_mir/util/pretty.rs [new file with mode: 0644]

index b15c1873f9bd84506dbe3cd7b3f5946bf83dad59..7f95f07f48d4ab3d6f062abb33a4aff6046ba779 100644 (file)
@@ -15,6 +15,7 @@
 use rustc_data_structures::bitslice::bits_to_string;
 use rustc_data_structures::indexed_set::{IdxSet};
 use rustc_data_structures::indexed_vec::Idx;
+use rustc_mir::util as mir_util;
 
 use dot;
 use dot::IntoCow;
@@ -219,7 +220,7 @@ fn chunked_present_left<W:io::Write>(w: &mut W,
             }
             Ok(())
         }
-        ::rustc_mir::graphviz::write_node_label(
+        mir_util::write_graphviz_node_label(
             *n, self.mbcx.mir(), &mut v, 4,
             |w| {
                 let flow = self.mbcx.flow_state();
index 7888a56d39dfbd5a5b3757160009e73acf2d7e61..da8aa231ccf157219d590059189e279d4e3605a8 100644 (file)
 use rustc_data_structures::bitslice::{BitwiseOperator};
 use rustc_data_structures::indexed_set::{IdxSet};
 use rustc_data_structures::indexed_vec::Idx;
+use rustc_mir::util::elaborate_drops::DropFlagState;
 
 use super::super::gather_moves::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex};
 use super::super::MoveDataParamEnv;
-use super::super::DropFlagState;
 use super::super::drop_flag_effects_for_function_entry;
 use super::super::drop_flag_effects_for_location;
 use super::super::on_lookup_result_bits;
index a71d23e7e1e7ecf98a717219448c53dc0e8fd4fd..88ec86cc95d614dfe461a30620d73dc6c5c9a064 100644 (file)
 use super::dataflow::{DataflowResults};
 use super::{drop_flag_effects_for_location, on_all_children_bits};
 use super::on_lookup_result_bits;
-use super::{DropFlagState, MoveDataParamEnv};
-use super::patch::MirPatch;
-use rustc::ty::{self, Ty, TyCtxt};
-use rustc::ty::subst::{Kind, Subst, Substs};
-use rustc::ty::util::IntTypeExt;
+use super::MoveDataParamEnv;
+use rustc::ty::{self, TyCtxt};
 use rustc::mir::*;
 use rustc::mir::transform::{Pass, MirPass, MirSource};
 use rustc::middle::const_val::ConstVal;
-use rustc::middle::lang_items;
 use rustc::util::nodemap::FxHashMap;
 use rustc_data_structures::indexed_set::IdxSetBuf;
 use rustc_data_structures::indexed_vec::Idx;
+use rustc_mir::util::patch::MirPatch;
+use rustc_mir::util::elaborate_drops::{DropFlagState, elaborate_drop};
+use rustc_mir::util::elaborate_drops::{DropElaborator, DropStyle, DropFlagMode};
 use syntax_pos::Span;
 
 use std::fmt;
-use std::iter;
 use std::u32;
 
 pub struct ElaborateDrops;
@@ -109,12 +107,116 @@ fn state(&self, path: MovePathIndex) -> (bool, bool) {
     }
 }
 
-impl fmt::Debug for InitializationData {
+struct Elaborator<'a, 'b: 'a, 'tcx: 'b> {
+    init_data: &'a InitializationData,
+    ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>,
+}
+
+impl<'a, 'b, 'tcx> fmt::Debug for Elaborator<'a, 'b, 'tcx> {
     fn fmt(&self, _f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
         Ok(())
     }
 }
 
+impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> {
+    type Path = MovePathIndex;
+
+    fn patch(&mut self) -> &mut MirPatch<'tcx> {
+        &mut self.ctxt.patch
+    }
+
+    fn mir(&self) -> &'a Mir<'tcx> {
+        self.ctxt.mir
+    }
+
+    fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx> {
+        self.ctxt.tcx
+    }
+
+    fn param_env(&self) -> &'a ty::ParameterEnvironment<'tcx> {
+        self.ctxt.param_env()
+    }
+
+    fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle {
+        let ((maybe_live, maybe_dead), multipart) = match mode {
+            DropFlagMode::Shallow => (self.init_data.state(path), false),
+            DropFlagMode::Deep => {
+                let mut some_live = false;
+                let mut some_dead = false;
+                let mut children_count = 0;
+                on_all_children_bits(
+                    self.tcx(), self.mir(), self.ctxt.move_data(),
+                    path, |child| {
+                        if self.ctxt.path_needs_drop(child) {
+                            let (live, dead) = self.init_data.state(child);
+                            debug!("elaborate_drop: state({:?}) = {:?}",
+                                   child, (live, dead));
+                            some_live |= live;
+                            some_dead |= dead;
+                            children_count += 1;
+                        }
+                    });
+                ((some_live, some_dead), children_count != 1)
+            }
+        };
+        match (maybe_live, maybe_dead, multipart) {
+            (false, _, _) => DropStyle::Dead,
+            (true, false, _) => DropStyle::Static,
+            (true, true, false) => DropStyle::Conditional,
+            (true, true, true) => DropStyle::Open,
+        }
+    }
+
+    fn clear_drop_flag(&mut self, loc: Location, path: Self::Path, mode: DropFlagMode) {
+        match mode {
+            DropFlagMode::Shallow => {
+                self.ctxt.set_drop_flag(loc, path, DropFlagState::Absent);
+            }
+            DropFlagMode::Deep => {
+                on_all_children_bits(
+                    self.tcx(), self.mir(), self.ctxt.move_data(), path,
+                    |child| self.ctxt.set_drop_flag(loc, child, DropFlagState::Absent)
+                 );
+            }
+        }
+    }
+
+    fn field_subpath(&self, path: Self::Path, field: Field) -> Option<Self::Path> {
+        super::move_path_children_matching(self.ctxt.move_data(), path, |p| {
+            match p {
+                &Projection {
+                    elem: ProjectionElem::Field(idx, _), ..
+                } => idx == field,
+                _ => false
+            }
+        })
+    }
+
+    fn deref_subpath(&self, path: Self::Path) -> Option<Self::Path> {
+        super::move_path_children_matching(self.ctxt.move_data(), path, |p| {
+            match p {
+                &Projection { elem: ProjectionElem::Deref, .. } => true,
+                _ => false
+            }
+        })
+    }
+
+    fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option<Self::Path> {
+        super::move_path_children_matching(self.ctxt.move_data(), path, |p| {
+            match p {
+                &Projection {
+                    elem: ProjectionElem::Downcast(_, idx), ..
+                } => idx == variant,
+                _ => false
+            }
+        })
+    }
+
+    fn get_drop_flag(&mut self, path: Self::Path) -> Option<Operand<'tcx>> {
+        self.ctxt.drop_flag(path).map(Operand::Consume)
+    }
+}
+
 struct ElaborateDropsCtxt<'a, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     mir: &'a Mir<'tcx>,
@@ -125,19 +227,6 @@ struct ElaborateDropsCtxt<'a, 'tcx: 'a> {
     patch: MirPatch<'tcx>,
 }
 
-#[derive(Copy, Clone, Debug)]
-struct DropCtxt<'a, 'tcx: 'a> {
-    source_info: SourceInfo,
-    is_cleanup: bool,
-
-    init_data: &'a InitializationData,
-
-    lvalue: &'a Lvalue<'tcx>,
-    path: MovePathIndex,
-    succ: BasicBlock,
-    unwind: Option<BasicBlock>
-}
-
 impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
     fn move_data(&self) -> &'b MoveData<'tcx> { &self.env.move_data }
     fn param_env(&self) -> &'b ty::ParameterEnvironment<'tcx> {
@@ -254,19 +343,22 @@ fn elaborate_drops(&mut self)
                     let init_data = self.initialization_data_at(loc);
                     match self.move_data().rev_lookup.find(location) {
                         LookupResult::Exact(path) => {
-                            self.elaborate_drop(&DropCtxt {
-                                source_info: terminator.source_info,
-                                is_cleanup: data.is_cleanup,
-                                init_data: &init_data,
-                                lvalue: location,
-                                path: path,
-                                succ: target,
-                                unwind: if data.is_cleanup {
+                            elaborate_drop(
+                                &mut Elaborator {
+                                    init_data: &init_data,
+                                    ctxt: self
+                                },
+                                terminator.source_info,
+                                data.is_cleanup,
+                                location,
+                                path,
+                                target,
+                                if data.is_cleanup {
                                     None
                                 } else {
                                     Some(Option::unwrap_or(unwind, resume_block))
-                                }
-                            }, bb);
+                                },
+                                bb)
                         }
                         LookupResult::Parent(..) => {
                             span_bug!(terminator.source_info.span,
@@ -343,15 +435,18 @@ fn elaborate_replace(
                 debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path);
                 let init_data = self.initialization_data_at(loc);
 
-                self.elaborate_drop(&DropCtxt {
-                    source_info: terminator.source_info,
-                    is_cleanup: data.is_cleanup,
-                    init_data: &init_data,
-                    lvalue: location,
-                    path: path,
-                    succ: target,
-                    unwind: Some(unwind)
-                }, bb);
+                elaborate_drop(
+                    &mut Elaborator {
+                        init_data: &init_data,
+                        ctxt: self
+                    },
+                    terminator.source_info,
+                    data.is_cleanup,
+                    location,
+                    path,
+                    target,
+                    Some(unwind),
+                    bb);
                 on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| {
                     self.set_drop_flag(Location { block: target, statement_index: 0 },
                                        child, DropFlagState::Present);
@@ -372,547 +467,6 @@ fn elaborate_replace(
         }
     }
 
-    /// This elaborates a single drop instruction, located at `bb`, and
-    /// patches over it.
-    ///
-    /// The elaborated drop checks the drop flags to only drop what
-    /// is initialized.
-    ///
-    /// In addition, the relevant drop flags also need to be cleared
-    /// to avoid double-drops. However, in the middle of a complex
-    /// drop, one must avoid clearing some of the flags before they
-    /// are read, as that would cause a memory leak.
-    ///
-    /// In particular, when dropping an ADT, multiple fields may be
-    /// joined together under the `rest` subpath. They are all controlled
-    /// by the primary drop flag, but only the last rest-field dropped
-    /// should clear it (and it must also not clear anything else).
-    ///
-    /// FIXME: I think we should just control the flags externally
-    /// and then we do not need this machinery.
-    fn elaborate_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, bb: BasicBlock) {
-        debug!("elaborate_drop({:?})", c);
-
-        let mut some_live = false;
-        let mut some_dead = false;
-        let mut children_count = 0;
-        on_all_children_bits(
-            self.tcx, self.mir, self.move_data(),
-            c.path, |child| {
-                if self.path_needs_drop(child) {
-                    let (live, dead) = c.init_data.state(child);
-                    debug!("elaborate_drop: state({:?}) = {:?}",
-                           child, (live, dead));
-                    some_live |= live;
-                    some_dead |= dead;
-                    children_count += 1;
-                }
-            });
-
-        debug!("elaborate_drop({:?}): live - {:?}", c,
-               (some_live, some_dead));
-        match (some_live, some_dead) {
-            (false, false) | (false, true) => {
-                // dead drop - patch it out
-                self.patch.patch_terminator(bb, TerminatorKind::Goto {
-                    target: c.succ
-                });
-            }
-            (true, false) => {
-                // static drop - just set the flag
-                self.patch.patch_terminator(bb, TerminatorKind::Drop {
-                    location: c.lvalue.clone(),
-                    target: c.succ,
-                    unwind: c.unwind
-                });
-                self.drop_flags_for_drop(c, bb);
-            }
-            (true, true) => {
-                // dynamic drop
-                let drop_bb = if children_count == 1 || self.must_complete_drop(c) {
-                    self.conditional_drop(c)
-                } else {
-                    self.open_drop(c)
-                };
-                self.patch.patch_terminator(bb, TerminatorKind::Goto {
-                    target: drop_bb
-                });
-            }
-        }
-    }
-
-    /// Return the lvalue and move path for each field of `variant`,
-    /// (the move path is `None` if the field is a rest field).
-    fn move_paths_for_fields(&self,
-                             base_lv: &Lvalue<'tcx>,
-                             variant_path: MovePathIndex,
-                             variant: &'tcx ty::VariantDef,
-                             substs: &'tcx Substs<'tcx>)
-                             -> Vec<(Lvalue<'tcx>, Option<MovePathIndex>)>
-    {
-        variant.fields.iter().enumerate().map(|(i, f)| {
-            let subpath =
-                super::move_path_children_matching(self.move_data(), variant_path, |p| {
-                    match p {
-                        &Projection {
-                            elem: ProjectionElem::Field(idx, _), ..
-                        } => idx.index() == i,
-                        _ => false
-                    }
-                });
-
-            let field_ty =
-                self.tcx.normalize_associated_type_in_env(
-                    &f.ty(self.tcx, substs),
-                    self.param_env()
-                );
-            (base_lv.clone().field(Field::new(i), field_ty), subpath)
-        }).collect()
-    }
-
-    /// Create one-half of the drop ladder for a list of fields, and return
-    /// the list of steps in it in reverse order.
-    ///
-    /// `unwind_ladder` is such a list of steps in reverse order,
-    /// which is called instead of the next step if the drop unwinds
-    /// (the first field is never reached). If it is `None`, all
-    /// unwind targets are left blank.
-    fn drop_halfladder<'a>(&mut self,
-                           c: &DropCtxt<'a, 'tcx>,
-                           unwind_ladder: Option<Vec<BasicBlock>>,
-                           succ: BasicBlock,
-                           fields: &[(Lvalue<'tcx>, Option<MovePathIndex>)],
-                           is_cleanup: bool)
-                           -> Vec<BasicBlock>
-    {
-        let mut unwind_succ = if is_cleanup {
-            None
-        } else {
-            c.unwind
-        };
-
-        let mut succ = self.new_block(
-            c, c.is_cleanup, TerminatorKind::Goto { target: succ }
-        );
-
-        // Always clear the "master" drop flag at the bottom of the
-        // ladder. This is needed because the "master" drop flag
-        // protects the ADT's discriminant, which is invalidated
-        // after the ADT is dropped.
-        self.set_drop_flag(
-            Location { block: succ, statement_index: 0 },
-            c.path,
-            DropFlagState::Absent
-        );
-
-        fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| {
-            succ = if let Some(path) = path {
-                debug!("drop_ladder: for std field {} ({:?})", i, lv);
-
-                self.elaborated_drop_block(&DropCtxt {
-                    source_info: c.source_info,
-                    is_cleanup: is_cleanup,
-                    init_data: c.init_data,
-                    lvalue: lv,
-                    path: path,
-                    succ: succ,
-                    unwind: unwind_succ,
-                })
-            } else {
-                debug!("drop_ladder: for rest field {} ({:?})", i, lv);
-
-                self.complete_drop(&DropCtxt {
-                    source_info: c.source_info,
-                    is_cleanup: is_cleanup,
-                    init_data: c.init_data,
-                    lvalue: lv,
-                    path: c.path,
-                    succ: succ,
-                    unwind: unwind_succ,
-                }, false)
-            };
-
-            unwind_succ = unwind_ladder.as_ref().map(|p| p[i]);
-            succ
-        }).collect()
-    }
-
-    /// Create a full drop ladder, consisting of 2 connected half-drop-ladders
-    ///
-    /// For example, with 3 fields, the drop ladder is
-    ///
-    /// .d0:
-    ///     ELAB(drop location.0 [target=.d1, unwind=.c1])
-    /// .d1:
-    ///     ELAB(drop location.1 [target=.d2, unwind=.c2])
-    /// .d2:
-    ///     ELAB(drop location.2 [target=`c.succ`, unwind=`c.unwind`])
-    /// .c1:
-    ///     ELAB(drop location.1 [target=.c2])
-    /// .c2:
-    ///     ELAB(drop location.2 [target=`c.unwind])
-    fn drop_ladder<'a>(&mut self,
-                       c: &DropCtxt<'a, 'tcx>,
-                       fields: Vec<(Lvalue<'tcx>, Option<MovePathIndex>)>)
-                       -> BasicBlock
-    {
-        debug!("drop_ladder({:?}, {:?})", c, fields);
-
-        let mut fields = fields;
-        fields.retain(|&(ref lvalue, _)| {
-            let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
-            self.tcx.type_needs_drop_given_env(ty, self.param_env())
-        });
-
-        debug!("drop_ladder - fields needing drop: {:?}", fields);
-
-        let unwind_ladder = if c.is_cleanup {
-            None
-        } else {
-            Some(self.drop_halfladder(c, None, c.unwind.unwrap(), &fields, true))
-        };
-
-        self.drop_halfladder(c, unwind_ladder, c.succ, &fields, c.is_cleanup)
-            .last().cloned().unwrap_or(c.succ)
-    }
-
-    fn open_drop_for_tuple<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, tys: &[Ty<'tcx>])
-                               -> BasicBlock
-    {
-        debug!("open_drop_for_tuple({:?}, {:?})", c, tys);
-
-        let fields = tys.iter().enumerate().map(|(i, &ty)| {
-            (c.lvalue.clone().field(Field::new(i), ty),
-             super::move_path_children_matching(
-                 self.move_data(), c.path, |proj| match proj {
-                     &Projection {
-                         elem: ProjectionElem::Field(f, _), ..
-                     } => f.index() == i,
-                     _ => false
-                 }
-            ))
-        }).collect();
-
-        self.drop_ladder(c, fields)
-    }
-
-    fn open_drop_for_box<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, ty: Ty<'tcx>)
-                             -> BasicBlock
-    {
-        debug!("open_drop_for_box({:?}, {:?})", c, ty);
-
-        let interior_path = super::move_path_children_matching(
-            self.move_data(), c.path, |proj| match proj {
-                &Projection { elem: ProjectionElem::Deref, .. } => true,
-                _ => false
-            }).unwrap();
-
-        let interior = c.lvalue.clone().deref();
-        let inner_c = DropCtxt {
-            lvalue: &interior,
-            unwind: c.unwind.map(|u| {
-                self.box_free_block(c, ty, u, true)
-            }),
-            succ: self.box_free_block(c, ty, c.succ, c.is_cleanup),
-            path: interior_path,
-            ..*c
-        };
-
-        self.elaborated_drop_block(&inner_c)
-    }
-
-    fn open_drop_for_adt<'a>(&mut self, c: &DropCtxt<'a, 'tcx>,
-                             adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>)
-                             -> BasicBlock {
-        debug!("open_drop_for_adt({:?}, {:?}, {:?})", c, adt, substs);
-
-        match adt.variants.len() {
-            1 => {
-                let fields = self.move_paths_for_fields(
-                    c.lvalue,
-                    c.path,
-                    &adt.variants[0],
-                    substs
-                );
-                self.drop_ladder(c, fields)
-            }
-            _ => {
-                let mut values = Vec::with_capacity(adt.variants.len());
-                let mut blocks = Vec::with_capacity(adt.variants.len());
-                let mut otherwise = None;
-                for (variant_index, discr) in adt.discriminants(self.tcx).enumerate() {
-                    let subpath = super::move_path_children_matching(
-                        self.move_data(), c.path, |proj| match proj {
-                            &Projection {
-                                elem: ProjectionElem::Downcast(_, idx), ..
-                            } => idx == variant_index,
-                            _ => false
-                        });
-                    if let Some(variant_path) = subpath {
-                        let base_lv = c.lvalue.clone().elem(
-                            ProjectionElem::Downcast(adt, variant_index)
-                        );
-                        let fields = self.move_paths_for_fields(
-                            &base_lv,
-                            variant_path,
-                            &adt.variants[variant_index],
-                            substs);
-                        values.push(discr);
-                        blocks.push(self.drop_ladder(c, fields));
-                    } else {
-                        // variant not found - drop the entire enum
-                        if let None = otherwise {
-                            otherwise = Some(self.complete_drop(c, true));
-                        }
-                    }
-                }
-                if let Some(block) = otherwise {
-                    blocks.push(block);
-                } else {
-                    values.pop();
-                }
-                // If there are multiple variants, then if something
-                // is present within the enum the discriminant, tracked
-                // by the rest path, must be initialized.
-                //
-                // Additionally, we do not want to switch on the
-                // discriminant after it is free-ed, because that
-                // way lies only trouble.
-                let discr_ty = adt.repr.discr_type().to_ty(self.tcx);
-                let discr = Lvalue::Local(self.patch.new_temp(discr_ty));
-                let switch_block = self.patch.new_block(BasicBlockData {
-                    statements: vec![
-                        Statement {
-                            source_info: c.source_info,
-                            kind: StatementKind::Assign(discr.clone(),
-                                                        Rvalue::Discriminant(c.lvalue.clone()))
-                        }
-                    ],
-                    terminator: Some(Terminator {
-                        source_info: c.source_info,
-                        kind: TerminatorKind::SwitchInt {
-                            discr: Operand::Consume(discr),
-                            switch_ty: discr_ty,
-                            values: From::from(values),
-                            targets: blocks,
-                        }
-                    }),
-                    is_cleanup: c.is_cleanup,
-                });
-                self.drop_flag_test_block(c, switch_block)
-            }
-        }
-    }
-
-    /// The slow-path - create an "open", elaborated drop for a type
-    /// which is moved-out-of only partially, and patch `bb` to a jump
-    /// to it. This must not be called on ADTs with a destructor,
-    /// as these can't be moved-out-of, except for `Box<T>`, which is
-    /// special-cased.
-    ///
-    /// This creates a "drop ladder" that drops the needed fields of the
-    /// ADT, both in the success case or if one of the destructors fail.
-    fn open_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock {
-        let ty = c.lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
-        match ty.sty {
-            ty::TyClosure(def_id, substs) => {
-                let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx).collect();
-                self.open_drop_for_tuple(c, &tys)
-            }
-            ty::TyTuple(tys, _) => {
-                self.open_drop_for_tuple(c, tys)
-            }
-            ty::TyAdt(def, _) if def.is_box() => {
-                self.open_drop_for_box(c, ty.boxed_ty())
-            }
-            ty::TyAdt(def, substs) => {
-                self.open_drop_for_adt(c, def, substs)
-            }
-            _ => bug!("open drop from non-ADT `{:?}`", ty)
-        }
-    }
-
-    /// Return a basic block that drop an lvalue using the context
-    /// and path in `c`. If `update_drop_flag` is true, also
-    /// clear `c`.
-    ///
-    /// if FLAG(c.path)
-    ///     if(update_drop_flag) FLAG(c.path) = false
-    ///     drop(c.lv)
-    fn complete_drop<'a>(
-        &mut self,
-        c: &DropCtxt<'a, 'tcx>,
-        update_drop_flag: bool)
-        -> BasicBlock
-    {
-        debug!("complete_drop({:?},{:?})", c, update_drop_flag);
-
-        let drop_block = self.drop_block(c);
-        if update_drop_flag {
-            self.set_drop_flag(
-                Location { block: drop_block, statement_index: 0 },
-                c.path,
-                DropFlagState::Absent
-            );
-        }
-
-        self.drop_flag_test_block(c, drop_block)
-    }
-
-    /// Create a simple conditional drop.
-    ///
-    /// if FLAG(c.lv)
-    ///     FLAGS(c.lv) = false
-    ///     drop(c.lv)
-    fn conditional_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>)
-                            -> BasicBlock
-    {
-        debug!("conditional_drop({:?})", c);
-        let drop_bb = self.drop_block(c);
-        self.drop_flags_for_drop(c, drop_bb);
-
-        self.drop_flag_test_block(c, drop_bb)
-    }
-
-    fn new_block<'a>(&mut self,
-                     c: &DropCtxt<'a, 'tcx>,
-                     is_cleanup: bool,
-                     k: TerminatorKind<'tcx>)
-                     -> BasicBlock
-    {
-        self.patch.new_block(BasicBlockData {
-            statements: vec![],
-            terminator: Some(Terminator {
-                source_info: c.source_info, kind: k
-            }),
-            is_cleanup: is_cleanup
-        })
-    }
-
-    fn elaborated_drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock {
-        debug!("elaborated_drop_block({:?})", c);
-        let blk = self.drop_block(c);
-        self.elaborate_drop(c, blk);
-        blk
-    }
-
-    fn drop_flag_test_block<'a>(&mut self,
-                                c: &DropCtxt<'a, 'tcx>,
-                                on_set: BasicBlock)
-                                -> BasicBlock {
-        self.drop_flag_test_block_with_succ(c, c.is_cleanup, on_set, c.succ)
-    }
-
-    fn drop_flag_test_block_with_succ<'a>(&mut self,
-                                          c: &DropCtxt<'a, 'tcx>,
-                                          is_cleanup: bool,
-                                          on_set: BasicBlock,
-                                          on_unset: BasicBlock)
-                                          -> BasicBlock
-    {
-        let (maybe_live, maybe_dead) = c.init_data.state(c.path);
-        debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}",
-               c, is_cleanup, on_set, (maybe_live, maybe_dead));
-
-        match (maybe_live, maybe_dead) {
-            (false, _) => on_unset,
-            (true, false) => on_set,
-            (true, true) => {
-                let flag = self.drop_flag(c.path).unwrap();
-                let term = TerminatorKind::if_(self.tcx, Operand::Consume(flag), on_set, on_unset);
-                self.new_block(c, is_cleanup, term)
-            }
-        }
-    }
-
-    fn drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock {
-        self.new_block(c, c.is_cleanup, TerminatorKind::Drop {
-            location: c.lvalue.clone(),
-            target: c.succ,
-            unwind: c.unwind
-        })
-    }
-
-    fn box_free_block<'a>(
-        &mut self,
-        c: &DropCtxt<'a, 'tcx>,
-        ty: Ty<'tcx>,
-        target: BasicBlock,
-        is_cleanup: bool
-    ) -> BasicBlock {
-        let block = self.unelaborated_free_block(c, ty, target, is_cleanup);
-        self.drop_flag_test_block_with_succ(c, is_cleanup, block, target)
-    }
-
-    fn unelaborated_free_block<'a>(
-        &mut self,
-        c: &DropCtxt<'a, 'tcx>,
-        ty: Ty<'tcx>,
-        target: BasicBlock,
-        is_cleanup: bool
-    ) -> BasicBlock {
-        let mut statements = vec![];
-        if let Some(&flag) = self.drop_flags.get(&c.path) {
-            statements.push(Statement {
-                source_info: c.source_info,
-                kind: StatementKind::Assign(
-                    Lvalue::Local(flag),
-                    self.constant_bool(c.source_info.span, false)
-                )
-            });
-        }
-
-        let tcx = self.tcx;
-        let unit_temp = Lvalue::Local(self.patch.new_temp(tcx.mk_nil()));
-        let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem);
-        let substs = tcx.mk_substs(iter::once(Kind::from(ty)));
-        let fty = tcx.item_type(free_func).subst(tcx, substs);
-
-        self.patch.new_block(BasicBlockData {
-            statements: statements,
-            terminator: Some(Terminator {
-                source_info: c.source_info, kind: TerminatorKind::Call {
-                    func: Operand::Constant(Constant {
-                        span: c.source_info.span,
-                        ty: fty,
-                        literal: Literal::Item {
-                            def_id: free_func,
-                            substs: substs
-                        }
-                    }),
-                    args: vec![Operand::Consume(c.lvalue.clone())],
-                    destination: Some((unit_temp, target)),
-                    cleanup: None
-                }
-            }),
-            is_cleanup: is_cleanup
-        })
-    }
-
-    fn must_complete_drop<'a>(&self, c: &DropCtxt<'a, 'tcx>) -> bool {
-        // if we have a destuctor, we must *not* split the drop.
-
-        // dataflow can create unneeded children in some cases
-        // - be sure to ignore them.
-
-        let ty = c.lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
-
-        match ty.sty {
-            ty::TyAdt(def, _) => {
-                if def.has_dtor(self.tcx) && !def.is_box() {
-                    self.tcx.sess.span_warn(
-                        c.source_info.span,
-                        &format!("dataflow bug??? moving out of type with dtor {:?}",
-                                 c));
-                    true
-                } else {
-                    false
-                }
-            }
-            _ => false
-        }
-    }
-
     fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> {
         Rvalue::Use(Operand::Constant(Constant {
             span: span,
@@ -1023,15 +577,4 @@ fn drop_flags_for_locs(&mut self) {
             }
         }
     }
-
-    fn drop_flags_for_drop<'a>(&mut self,
-                               c: &DropCtxt<'a, 'tcx>,
-                               bb: BasicBlock)
-    {
-        let loc = self.patch.terminator_loc(self.mir, bb);
-        on_all_children_bits(
-            self.tcx, self.mir, self.move_data(), c.path,
-            |child| self.set_drop_flag(loc, child, DropFlagState::Absent)
-        );
-    }
 }
index 1c9ee335699ae13439d689cbb1af77e826b72b69..9237bb31f6bd7f62c267550d29723c46d69bcf13 100644 (file)
 use rustc::mir::{self, BasicBlock, BasicBlockData, Mir, Statement, Terminator, Location};
 use rustc::session::Session;
 use rustc::ty::{self, TyCtxt};
+use rustc_mir::util::elaborate_drops::DropFlagState;
 
 mod abs_domain;
 pub mod elaborate_drops;
 mod dataflow;
 mod gather_moves;
-mod patch;
 // mod graphviz;
 
 use self::dataflow::{BitDenotation};
@@ -183,21 +183,6 @@ fn process_terminator(&mut self, bb: BasicBlock, term: &Option<Terminator<'tcx>>
     }
 }
 
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-enum DropFlagState {
-    Present, // i.e. initialized
-    Absent, // i.e. deinitialized or "moved"
-}
-
-impl DropFlagState {
-    fn value(self) -> bool {
-        match self {
-            DropFlagState::Present => true,
-            DropFlagState::Absent => false
-        }
-    }
-}
-
 fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
                                         path: MovePathIndex,
                                         mut cond: F)
diff --git a/src/librustc_borrowck/borrowck/mir/patch.rs b/src/librustc_borrowck/borrowck/mir/patch.rs
deleted file mode 100644 (file)
index 19f240d..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2016 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 rustc::ty::Ty;
-use rustc::mir::*;
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
-
-/// This struct represents a patch to MIR, which can add
-/// new statements and basic blocks and patch over block
-/// terminators.
-pub struct MirPatch<'tcx> {
-    patch_map: IndexVec<BasicBlock, Option<TerminatorKind<'tcx>>>,
-    new_blocks: Vec<BasicBlockData<'tcx>>,
-    new_statements: Vec<(Location, StatementKind<'tcx>)>,
-    new_locals: Vec<LocalDecl<'tcx>>,
-    resume_block: BasicBlock,
-    next_local: usize,
-}
-
-impl<'tcx> MirPatch<'tcx> {
-    pub fn new(mir: &Mir<'tcx>) -> Self {
-        let mut result = MirPatch {
-            patch_map: IndexVec::from_elem(None, mir.basic_blocks()),
-            new_blocks: vec![],
-            new_statements: vec![],
-            new_locals: vec![],
-            next_local: mir.local_decls.len(),
-            resume_block: START_BLOCK
-        };
-
-        // make sure the MIR we create has a resume block. It is
-        // completely legal to convert jumps to the resume block
-        // to jumps to None, but we occasionally have to add
-        // instructions just before that.
-
-        let mut resume_block = None;
-        let mut resume_stmt_block = None;
-        for (bb, block) in mir.basic_blocks().iter_enumerated() {
-            if let TerminatorKind::Resume = block.terminator().kind {
-                if block.statements.len() > 0 {
-                    resume_stmt_block = Some(bb);
-                } else {
-                    resume_block = Some(bb);
-                }
-                break
-            }
-        }
-        let resume_block = resume_block.unwrap_or_else(|| {
-            result.new_block(BasicBlockData {
-                statements: vec![],
-                terminator: Some(Terminator {
-                    source_info: SourceInfo {
-                        span: mir.span,
-                        scope: ARGUMENT_VISIBILITY_SCOPE
-                    },
-                    kind: TerminatorKind::Resume
-                }),
-                is_cleanup: true
-            })});
-        result.resume_block = resume_block;
-        if let Some(resume_stmt_block) = resume_stmt_block {
-            result.patch_terminator(resume_stmt_block, TerminatorKind::Goto {
-                target: resume_block
-            });
-        }
-        result
-    }
-
-    pub fn resume_block(&self) -> BasicBlock {
-        self.resume_block
-    }
-
-    pub fn is_patched(&self, bb: BasicBlock) -> bool {
-        self.patch_map[bb].is_some()
-    }
-
-    pub fn terminator_loc(&self, mir: &Mir<'tcx>, bb: BasicBlock) -> Location {
-        let offset = match bb.index().checked_sub(mir.basic_blocks().len()) {
-            Some(index) => self.new_blocks[index].statements.len(),
-            None => mir[bb].statements.len()
-        };
-        Location {
-            block: bb,
-            statement_index: offset
-        }
-    }
-
-    pub fn new_temp(&mut self, ty: Ty<'tcx>) -> Local {
-        let index = self.next_local;
-        self.next_local += 1;
-        self.new_locals.push(LocalDecl::new_temp(ty));
-        Local::new(index as usize)
-    }
-
-    pub fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {
-        let block = BasicBlock::new(self.patch_map.len());
-        debug!("MirPatch: new_block: {:?}: {:?}", block, data);
-        self.new_blocks.push(data);
-        self.patch_map.push(None);
-        block
-    }
-
-    pub fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) {
-        assert!(self.patch_map[block].is_none());
-        debug!("MirPatch: patch_terminator({:?}, {:?})", block, new);
-        self.patch_map[block] = Some(new);
-    }
-
-    pub fn add_statement(&mut self, loc: Location, stmt: StatementKind<'tcx>) {
-        debug!("MirPatch: add_statement({:?}, {:?})", loc, stmt);
-        self.new_statements.push((loc, stmt));
-    }
-
-    pub fn add_assign(&mut self, loc: Location, lv: Lvalue<'tcx>, rv: Rvalue<'tcx>) {
-        self.add_statement(loc, StatementKind::Assign(lv, rv));
-    }
-
-    pub fn apply(self, mir: &mut Mir<'tcx>) {
-        debug!("MirPatch: {:?} new temps, starting from index {}: {:?}",
-               self.new_locals.len(), mir.local_decls.len(), self.new_locals);
-        debug!("MirPatch: {} new blocks, starting from index {}",
-               self.new_blocks.len(), mir.basic_blocks().len());
-        mir.basic_blocks_mut().extend(self.new_blocks);
-        mir.local_decls.extend(self.new_locals);
-        for (src, patch) in self.patch_map.into_iter_enumerated() {
-            if let Some(patch) = patch {
-                debug!("MirPatch: patching block {:?}", src);
-                mir[src].terminator_mut().kind = patch;
-            }
-        }
-
-        let mut new_statements = self.new_statements;
-        new_statements.sort_by(|u,v| u.0.cmp(&v.0));
-
-        let mut delta = 0;
-        let mut last_bb = START_BLOCK;
-        for (mut loc, stmt) in new_statements {
-            if loc.block != last_bb {
-                delta = 0;
-                last_bb = loc.block;
-            }
-            debug!("MirPatch: adding statement {:?} at loc {:?}+{}",
-                   stmt, loc, delta);
-            loc.statement_index += delta;
-            let source_info = Self::source_info_for_index(
-                &mir[loc.block], loc
-            );
-            mir[loc.block].statements.insert(
-                loc.statement_index, Statement {
-                    source_info: source_info,
-                    kind: stmt
-                });
-            delta += 1;
-        }
-    }
-
-    pub fn source_info_for_index(data: &BasicBlockData, loc: Location) -> SourceInfo {
-        match data.statements.get(loc.statement_index) {
-            Some(stmt) => stmt.source_info,
-            None => data.terminator().source_info
-        }
-    }
-
-    pub fn source_info_for_location(&self, mir: &Mir, loc: Location) -> SourceInfo {
-        let data = match loc.block.index().checked_sub(mir.basic_blocks().len()) {
-            Some(new) => &self.new_blocks[new],
-            None => &mir[loc.block]
-        };
-        Self::source_info_for_index(data, loc)
-    }
-}
index b6978478085d5b224607c90a0acc898c17ad712b..6cd97e9559885048ac4f6905ecc257b5bf45e545 100644 (file)
@@ -26,8 +26,7 @@
 use rustc_borrowck as borrowck;
 use rustc_borrowck::graphviz as borrowck_dot;
 
-use rustc_mir::pretty::write_mir_pretty;
-use rustc_mir::graphviz::write_mir_graphviz;
+use rustc_mir::util::{write_mir_pretty, write_mir_graphviz};
 
 use syntax::ast::{self, BlockCheckMode};
 use syntax::fold::{self, Folder};
diff --git a/src/librustc_mir/def_use.rs b/src/librustc_mir/def_use.rs
deleted file mode 100644 (file)
index d20d50c..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2016 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.
-
-//! Def-use analysis.
-
-use rustc::mir::{Local, Location, Lvalue, Mir};
-use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
-use rustc_data_structures::indexed_vec::IndexVec;
-use std::marker::PhantomData;
-use std::mem;
-
-pub struct DefUseAnalysis<'tcx> {
-    info: IndexVec<Local, Info<'tcx>>,
-}
-
-#[derive(Clone)]
-pub struct Info<'tcx> {
-    pub defs_and_uses: Vec<Use<'tcx>>,
-}
-
-#[derive(Clone)]
-pub struct Use<'tcx> {
-    pub context: LvalueContext<'tcx>,
-    pub location: Location,
-}
-
-impl<'tcx> DefUseAnalysis<'tcx> {
-    pub fn new(mir: &Mir<'tcx>) -> DefUseAnalysis<'tcx> {
-        DefUseAnalysis {
-            info: IndexVec::from_elem_n(Info::new(), mir.local_decls.len()),
-        }
-    }
-
-    pub fn analyze(&mut self, mir: &Mir<'tcx>) {
-        let mut finder = DefUseFinder {
-            info: mem::replace(&mut self.info, IndexVec::new()),
-        };
-        finder.visit_mir(mir);
-        self.info = finder.info
-    }
-
-    pub fn local_info(&self, local: Local) -> &Info<'tcx> {
-        &self.info[local]
-    }
-
-    pub fn local_info_mut(&mut self, local: Local) -> &mut Info<'tcx> {
-        &mut self.info[local]
-    }
-
-    fn mutate_defs_and_uses<F>(&self, local: Local, mir: &mut Mir<'tcx>, mut callback: F)
-                               where F: for<'a> FnMut(&'a mut Lvalue<'tcx>,
-                                                      LvalueContext<'tcx>,
-                                                      Location) {
-        for lvalue_use in &self.info[local].defs_and_uses {
-            MutateUseVisitor::new(local,
-                                  &mut callback,
-                                  mir).visit_location(mir, lvalue_use.location)
-        }
-    }
-
-    /// FIXME(pcwalton): This should update the def-use chains.
-    pub fn replace_all_defs_and_uses_with(&self,
-                                          local: Local,
-                                          mir: &mut Mir<'tcx>,
-                                          new_lvalue: Lvalue<'tcx>) {
-        self.mutate_defs_and_uses(local, mir, |lvalue, _, _| *lvalue = new_lvalue.clone())
-    }
-}
-
-struct DefUseFinder<'tcx> {
-    info: IndexVec<Local, Info<'tcx>>,
-}
-
-impl<'tcx> DefUseFinder<'tcx> {
-    fn lvalue_mut_info(&mut self, lvalue: &Lvalue<'tcx>) -> Option<&mut Info<'tcx>> {
-        let info = &mut self.info;
-
-        if let Lvalue::Local(local) = *lvalue {
-            Some(&mut info[local])
-        } else {
-            None
-        }
-    }
-}
-
-impl<'tcx> Visitor<'tcx> for DefUseFinder<'tcx> {
-    fn visit_lvalue(&mut self,
-                    lvalue: &Lvalue<'tcx>,
-                    context: LvalueContext<'tcx>,
-                    location: Location) {
-        if let Some(ref mut info) = self.lvalue_mut_info(lvalue) {
-            info.defs_and_uses.push(Use {
-                context: context,
-                location: location,
-            })
-        }
-        self.super_lvalue(lvalue, context, location)
-    }
-}
-
-impl<'tcx> Info<'tcx> {
-    fn new() -> Info<'tcx> {
-        Info {
-            defs_and_uses: vec![],
-        }
-    }
-
-    pub fn def_count(&self) -> usize {
-        self.defs_and_uses.iter().filter(|lvalue_use| lvalue_use.context.is_mutating_use()).count()
-    }
-
-    pub fn def_count_not_including_drop(&self) -> usize {
-        self.defs_and_uses.iter().filter(|lvalue_use| {
-            lvalue_use.context.is_mutating_use() && !lvalue_use.context.is_drop()
-        }).count()
-    }
-
-    pub fn use_count(&self) -> usize {
-        self.defs_and_uses.iter().filter(|lvalue_use| {
-            lvalue_use.context.is_nonmutating_use()
-        }).count()
-    }
-}
-
-struct MutateUseVisitor<'tcx, F> {
-    query: Local,
-    callback: F,
-    phantom: PhantomData<&'tcx ()>,
-}
-
-impl<'tcx, F> MutateUseVisitor<'tcx, F> {
-    fn new(query: Local, callback: F, _: &Mir<'tcx>)
-           -> MutateUseVisitor<'tcx, F>
-           where F: for<'a> FnMut(&'a mut Lvalue<'tcx>, LvalueContext<'tcx>, Location) {
-        MutateUseVisitor {
-            query: query,
-            callback: callback,
-            phantom: PhantomData,
-        }
-    }
-}
-
-impl<'tcx, F> MutVisitor<'tcx> for MutateUseVisitor<'tcx, F>
-              where F: for<'a> FnMut(&'a mut Lvalue<'tcx>, LvalueContext<'tcx>, Location) {
-    fn visit_lvalue(&mut self,
-                    lvalue: &mut Lvalue<'tcx>,
-                    context: LvalueContext<'tcx>,
-                    location: Location) {
-        if let Lvalue::Local(local) = *lvalue {
-            if local == self.query {
-                (self.callback)(lvalue, context, location)
-            }
-        }
-        self.super_lvalue(lvalue, context, location)
-    }
-}
diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/graphviz.rs
deleted file mode 100644 (file)
index 91600b9..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 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 dot;
-use rustc::hir::def_id::DefId;
-use rustc::mir::*;
-use rustc::ty::TyCtxt;
-use std::fmt::Debug;
-use std::io::{self, Write};
-use syntax::ast::NodeId;
-
-use rustc_data_structures::indexed_vec::Idx;
-
-/// Write a graphviz DOT graph of a list of MIRs.
-pub fn write_mir_graphviz<'a, 'b, 'tcx, W, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
-                                              iter: I,
-                                              w: &mut W)
-                                              -> io::Result<()>
-    where W: Write, I: Iterator<Item=DefId>
-{
-    for def_id in iter {
-        let nodeid = tcx.hir.as_local_node_id(def_id).unwrap();
-        let mir = &tcx.item_mir(def_id);
-
-        writeln!(w, "digraph Mir_{} {{", nodeid)?;
-
-        // Global graph properties
-        writeln!(w, r#"    graph [fontname="monospace"];"#)?;
-        writeln!(w, r#"    node [fontname="monospace"];"#)?;
-        writeln!(w, r#"    edge [fontname="monospace"];"#)?;
-
-        // Graph label
-        write_graph_label(tcx, nodeid, mir, w)?;
-
-        // Nodes
-        for (block, _) in mir.basic_blocks().iter_enumerated() {
-            write_node(block, mir, w)?;
-        }
-
-        // Edges
-        for (source, _) in mir.basic_blocks().iter_enumerated() {
-            write_edges(source, mir, w)?;
-        }
-        writeln!(w, "}}")?
-    }
-    Ok(())
-}
-
-/// Write a graphviz HTML-styled label for the given basic block, with
-/// all necessary escaping already performed. (This is suitable for
-/// emitting directly, as is done in this module, or for use with the
-/// LabelText::HtmlStr from libgraphviz.)
-///
-/// `init` and `fini` are callbacks for emitting additional rows of
-/// data (using HTML enclosed with `<tr>` in the emitted text).
-pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
-                                              mir: &Mir,
-                                              w: &mut W,
-                                              num_cols: u32,
-                                              init: INIT,
-                                              fini: FINI) -> io::Result<()>
-    where INIT: Fn(&mut W) -> io::Result<()>,
-          FINI: Fn(&mut W) -> io::Result<()>
-{
-    let data = &mir[block];
-
-    write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
-
-    // Basic block number at the top.
-    write!(w, r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#,
-           attrs=r#"bgcolor="gray" align="center""#,
-           colspan=num_cols,
-           blk=block.index())?;
-
-    init(w)?;
-
-    // List of statements in the middle.
-    if !data.statements.is_empty() {
-        write!(w, r#"<tr><td align="left" balign="left">"#)?;
-        for statement in &data.statements {
-            write!(w, "{}<br/>", escape(statement))?;
-        }
-        write!(w, "</td></tr>")?;
-    }
-
-    // Terminator head at the bottom, not including the list of successor blocks. Those will be
-    // displayed as labels on the edges between blocks.
-    let mut terminator_head = String::new();
-    data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
-    write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
-
-    fini(w)?;
-
-    // Close the table
-    writeln!(w, "</table>")
-}
-
-/// Write a graphviz DOT node for the given basic block.
-fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
-    // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
-    write!(w, r#"    {} [shape="none", label=<"#, node(block))?;
-    write_node_label(block, mir, w, 1, |_| Ok(()), |_| Ok(()))?;
-    // Close the node label and the node itself.
-    writeln!(w, ">];")
-}
-
-/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
-fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
-    let terminator = mir[source].terminator();
-    let labels = terminator.kind.fmt_successor_labels();
-
-    for (&target, label) in terminator.successors().iter().zip(labels) {
-        writeln!(w, r#"    {} -> {} [label="{}"];"#, node(source), node(target), label)?;
-    }
-
-    Ok(())
-}
-
-/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
-/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
-/// all the variables and temporaries.
-fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                         nid: NodeId,
-                                         mir: &Mir,
-                                         w: &mut W)
-                                         -> io::Result<()> {
-    write!(w, "    label=<fn {}(", dot::escape_html(&tcx.node_path_str(nid)))?;
-
-    // fn argument types.
-    for (i, arg) in mir.args_iter().enumerate() {
-        if i > 0 {
-            write!(w, ", ")?;
-        }
-        write!(w, "{:?}: {}", Lvalue::Local(arg), escape(&mir.local_decls[arg].ty))?;
-    }
-
-    write!(w, ") -&gt; {}", escape(mir.return_ty))?;
-    write!(w, r#"<br align="left"/>"#)?;
-
-    for local in mir.vars_and_temps_iter() {
-        let decl = &mir.local_decls[local];
-
-        write!(w, "let ")?;
-        if decl.mutability == Mutability::Mut {
-            write!(w, "mut ")?;
-        }
-
-        if let Some(name) = decl.name {
-            write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
-                   Lvalue::Local(local), escape(&decl.ty), name)?;
-        } else {
-            write!(w, r#"let mut {:?}: {};<br align="left"/>"#,
-                   Lvalue::Local(local), escape(&decl.ty))?;
-        }
-    }
-
-    writeln!(w, ">;")
-}
-
-fn node(block: BasicBlock) -> String {
-    format!("bb{}", block.index())
-}
-
-fn escape<T: Debug>(t: &T) -> String {
-    dot::escape_html(&format!("{:?}", t))
-}
index 2718a0204a1ed07926a3921d5cf2e245617cf28a..590c6a430b98a2862970c58db9c28c8282bc50d5 100644 (file)
 
 pub mod build;
 pub mod callgraph;
-pub mod def_use;
-pub mod graphviz;
 mod hair;
 mod shim;
 pub mod mir_map;
-pub mod pretty;
 pub mod transform;
+pub mod util;
 
 use rustc::ty::maps::Providers;
 
index 2d2b90235ca3b6b6392cab0bcd378a42fd9f8f34..8c138d779c10687f274897e635476808e1227de5 100644 (file)
@@ -23,8 +23,8 @@
 use rustc::mir::transform::MirSource;
 use rustc::mir::visit::MutVisitor;
 use shim;
-use pretty;
 use hair::cx::Cx;
+use util as mir_util;
 
 use rustc::traits::Reveal;
 use rustc::ty::{self, Ty, TyCtxt};
@@ -175,7 +175,7 @@ fn build_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
             mem::transmute::<Mir, Mir<'tcx>>(mir)
         };
 
-        pretty::dump_mir(tcx, "mir_map", &0, src, &mir);
+        mir_util::dump_mir(tcx, "mir_map", &0, src, &mir);
 
         tcx.alloc_mir(mir)
     })
@@ -234,7 +234,7 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 mem::transmute::<Mir, Mir<'tcx>>(mir)
             };
 
-            pretty::dump_mir(tcx, "mir_map", &0, src, &mir);
+            mir_util::dump_mir(tcx, "mir_map", &0, src, &mir);
 
             tcx.alloc_mir(mir)
         })
diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs
deleted file mode 100644 (file)
index 35734dc..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-// Copyright 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 rustc::hir;
-use rustc::hir::def_id::DefId;
-use rustc::mir::*;
-use rustc::mir::transform::MirSource;
-use rustc::ty::TyCtxt;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::{Idx};
-use std::fmt::Display;
-use std::fs;
-use std::io::{self, Write};
-use std::path::{PathBuf, Path};
-
-const INDENT: &'static str = "    ";
-/// Alignment for lining up comments following MIR statements
-const ALIGN: usize = 40;
-
-/// If the session is properly configured, dumps a human-readable
-/// representation of the mir into:
-///
-/// ```text
-/// rustc.node<node_id>.<pass_name>.<disambiguator>
-/// ```
-///
-/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
-/// where `<filter>` takes the following forms:
-///
-/// - `all` -- dump MIR for all fns, all passes, all everything
-/// - `substring1&substring2,...` -- `&`-separated list of substrings
-///   that can appear in the pass-name or the `item_path_str` for the given
-///   node-id. If any one of the substrings match, the data is dumped out.
-pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                          pass_name: &str,
-                          disambiguator: &Display,
-                          src: MirSource,
-                          mir: &Mir<'tcx>) {
-    let filters = match tcx.sess.opts.debugging_opts.dump_mir {
-        None => return,
-        Some(ref filters) => filters,
-    };
-    let node_id = src.item_id();
-    let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id));
-    let is_matched =
-        filters.split("&")
-               .any(|filter| {
-                   filter == "all" ||
-                       pass_name.contains(filter) ||
-                       node_path.contains(filter)
-               });
-    if !is_matched {
-        return;
-    }
-
-    let promotion_id = match src {
-        MirSource::Promoted(_, id) => format!("-{:?}", id),
-        _ => String::new()
-    };
-
-    let mut file_path = PathBuf::new();
-    if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
-        let p = Path::new(file_dir);
-        file_path.push(p);
-    };
-    let file_name = format!("rustc.node{}{}.{}.{}.mir",
-                            node_id, promotion_id, pass_name, disambiguator);
-    file_path.push(&file_name);
-    let _ = fs::File::create(&file_path).and_then(|mut file| {
-        writeln!(file, "// MIR for `{}`", node_path)?;
-        writeln!(file, "// node_id = {}", node_id)?;
-        writeln!(file, "// pass_name = {}", pass_name)?;
-        writeln!(file, "// disambiguator = {}", disambiguator)?;
-        writeln!(file, "")?;
-        write_mir_fn(tcx, src, mir, &mut file)?;
-        Ok(())
-    });
-}
-
-/// Write out a human-readable textual representation for the given MIR.
-pub fn write_mir_pretty<'a, 'b, 'tcx, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
-                                         iter: I,
-                                         w: &mut Write)
-                                         -> io::Result<()>
-    where I: Iterator<Item=DefId>, 'tcx: 'a
-{
-    let mut first = true;
-    for def_id in iter.filter(DefId::is_local) {
-        let mir = &tcx.item_mir(def_id);
-
-        if first {
-            first = false;
-        } else {
-            // Put empty lines between all items
-            writeln!(w, "")?;
-        }
-
-        let id = tcx.hir.as_local_node_id(def_id).unwrap();
-        let src = MirSource::from_node(tcx, id);
-        write_mir_fn(tcx, src, mir, w)?;
-
-        for (i, mir) in mir.promoted.iter_enumerated() {
-            writeln!(w, "")?;
-            write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w)?;
-        }
-    }
-    Ok(())
-}
-
-pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                              src: MirSource,
-                              mir: &Mir<'tcx>,
-                              w: &mut Write)
-                              -> io::Result<()> {
-    write_mir_intro(tcx, src, mir, w)?;
-    for block in mir.basic_blocks().indices() {
-        write_basic_block(tcx, block, mir, w)?;
-        if block.index() + 1 != mir.basic_blocks().len() {
-            writeln!(w, "")?;
-        }
-    }
-
-    writeln!(w, "}}")?;
-    Ok(())
-}
-
-/// Write out a human-readable textual representation for the given basic block.
-fn write_basic_block(tcx: TyCtxt,
-                     block: BasicBlock,
-                     mir: &Mir,
-                     w: &mut Write)
-                     -> io::Result<()> {
-    let data = &mir[block];
-
-    // Basic block label at the top.
-    writeln!(w, "{}{:?}: {{", INDENT, block)?;
-
-    // List of statements in the middle.
-    let mut current_location = Location { block: block, statement_index: 0 };
-    for statement in &data.statements {
-        let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
-        writeln!(w, "{0:1$} // {2}",
-                 indented_mir,
-                 ALIGN,
-                 comment(tcx, statement.source_info))?;
-
-        current_location.statement_index += 1;
-    }
-
-    // Terminator at the bottom.
-    let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
-    writeln!(w, "{0:1$} // {2}",
-             indented_terminator,
-             ALIGN,
-             comment(tcx, data.terminator().source_info))?;
-
-    writeln!(w, "{}}}", INDENT)
-}
-
-fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
-    format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
-}
-
-/// Prints user-defined variables in a scope tree.
-///
-/// Returns the total number of variables printed.
-fn write_scope_tree(tcx: TyCtxt,
-                    mir: &Mir,
-                    scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
-                    w: &mut Write,
-                    parent: VisibilityScope,
-                    depth: usize)
-                    -> io::Result<()> {
-    let indent = depth * INDENT.len();
-
-    let children = match scope_tree.get(&parent) {
-        Some(childs) => childs,
-        None => return Ok(()),
-    };
-
-    for &child in children {
-        let data = &mir.visibility_scopes[child];
-        assert_eq!(data.parent_scope, Some(parent));
-        writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
-
-        // User variable types (including the user's name in a comment).
-        for local in mir.vars_iter() {
-            let var = &mir.local_decls[local];
-            let (name, source_info) = if var.source_info.unwrap().scope == child {
-                (var.name.unwrap(), var.source_info.unwrap())
-            } else {
-                // Not a variable or not declared in this scope.
-                continue;
-            };
-
-            let mut_str = if var.mutability == Mutability::Mut {
-                "mut "
-            } else {
-                ""
-            };
-
-            let indent = indent + INDENT.len();
-            let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
-                                       INDENT,
-                                       indent,
-                                       mut_str,
-                                       local,
-                                       var.ty);
-            writeln!(w, "{0:1$} // \"{2}\" in {3}",
-                     indented_var,
-                     ALIGN,
-                     name,
-                     comment(tcx, source_info))?;
-        }
-
-        write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
-
-        writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
-    }
-
-    Ok(())
-}
-
-/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
-/// local variables (both user-defined bindings and compiler temporaries).
-fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                             src: MirSource,
-                             mir: &Mir,
-                             w: &mut Write)
-                             -> io::Result<()> {
-    write_mir_sig(tcx, src, mir, w)?;
-    writeln!(w, " {{")?;
-
-    // construct a scope tree and write it out
-    let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
-    for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
-        if let Some(parent) = scope_data.parent_scope {
-            scope_tree.entry(parent)
-                      .or_insert(vec![])
-                      .push(VisibilityScope::new(index));
-        } else {
-            // Only the argument scope has no parent, because it's the root.
-            assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
-        }
-    }
-
-    // Print return pointer
-    let indented_retptr = format!("{}let mut {:?}: {};",
-                                  INDENT,
-                                  RETURN_POINTER,
-                                  mir.return_ty);
-    writeln!(w, "{0:1$} // return pointer",
-             indented_retptr,
-             ALIGN)?;
-
-    write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
-
-    write_temp_decls(mir, w)?;
-
-    // Add an empty line before the first block is printed.
-    writeln!(w, "")?;
-
-    Ok(())
-}
-
-fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
-                 -> io::Result<()>
-{
-    match src {
-        MirSource::Fn(_) => write!(w, "fn")?,
-        MirSource::Const(_) => write!(w, "const")?,
-        MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
-        MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
-        MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
-    }
-
-    write!(w, " {}", tcx.node_path_str(src.item_id()))?;
-
-    if let MirSource::Fn(_) = src {
-        write!(w, "(")?;
-
-        // fn argument types.
-        for (i, arg) in mir.args_iter().enumerate() {
-            if i != 0 {
-                write!(w, ", ")?;
-            }
-            write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
-        }
-
-        write!(w, ") -> {}", mir.return_ty)
-    } else {
-        assert_eq!(mir.arg_count, 0);
-        write!(w, ": {} =", mir.return_ty)
-    }
-}
-
-fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
-    // Compiler-introduced temporary types.
-    for temp in mir.temps_iter() {
-        writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
-    }
-
-    Ok(())
-}
index 2194c20c2f442fe3e62dff7771f6c5623c4fb3b7..5d127a5aed4618d0c5c76e8380f2eb744e346120 100644 (file)
 //! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the
 //! future.
 
-use def_use::DefUseAnalysis;
 use rustc::mir::{Constant, Local, LocalKind, Location, Lvalue, Mir, Operand, Rvalue, StatementKind};
 use rustc::mir::transform::{MirPass, MirSource, Pass};
 use rustc::mir::visit::MutVisitor;
 use rustc::ty::TyCtxt;
+use util::def_use::DefUseAnalysis;
 use transform::qualify_consts;
 
 pub struct CopyPropagation;
index 035f33de91aa5d15dca3bcc9048046bc5d20918c..f22a71636a98e883028c090f294e964be3dea271 100644 (file)
@@ -15,7 +15,7 @@
 use rustc::ty::TyCtxt;
 use rustc::mir::*;
 use rustc::mir::transform::{Pass, MirPass, MirPassHook, MirSource};
-use pretty;
+use util as mir_util;
 
 pub struct Marker<'a>(pub &'a str);
 
@@ -56,7 +56,7 @@ fn on_mir_pass<'a>(
         pass: &Pass,
         is_after: bool)
     {
-        pretty::dump_mir(
+        mir_util::dump_mir(
             tcx,
             &*pass.name(),
             &Disambiguator {
diff --git a/src/librustc_mir/util/def_use.rs b/src/librustc_mir/util/def_use.rs
new file mode 100644 (file)
index 0000000..d20d50c
--- /dev/null
@@ -0,0 +1,163 @@
+// Copyright 2016 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.
+
+//! Def-use analysis.
+
+use rustc::mir::{Local, Location, Lvalue, Mir};
+use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
+use rustc_data_structures::indexed_vec::IndexVec;
+use std::marker::PhantomData;
+use std::mem;
+
+pub struct DefUseAnalysis<'tcx> {
+    info: IndexVec<Local, Info<'tcx>>,
+}
+
+#[derive(Clone)]
+pub struct Info<'tcx> {
+    pub defs_and_uses: Vec<Use<'tcx>>,
+}
+
+#[derive(Clone)]
+pub struct Use<'tcx> {
+    pub context: LvalueContext<'tcx>,
+    pub location: Location,
+}
+
+impl<'tcx> DefUseAnalysis<'tcx> {
+    pub fn new(mir: &Mir<'tcx>) -> DefUseAnalysis<'tcx> {
+        DefUseAnalysis {
+            info: IndexVec::from_elem_n(Info::new(), mir.local_decls.len()),
+        }
+    }
+
+    pub fn analyze(&mut self, mir: &Mir<'tcx>) {
+        let mut finder = DefUseFinder {
+            info: mem::replace(&mut self.info, IndexVec::new()),
+        };
+        finder.visit_mir(mir);
+        self.info = finder.info
+    }
+
+    pub fn local_info(&self, local: Local) -> &Info<'tcx> {
+        &self.info[local]
+    }
+
+    pub fn local_info_mut(&mut self, local: Local) -> &mut Info<'tcx> {
+        &mut self.info[local]
+    }
+
+    fn mutate_defs_and_uses<F>(&self, local: Local, mir: &mut Mir<'tcx>, mut callback: F)
+                               where F: for<'a> FnMut(&'a mut Lvalue<'tcx>,
+                                                      LvalueContext<'tcx>,
+                                                      Location) {
+        for lvalue_use in &self.info[local].defs_and_uses {
+            MutateUseVisitor::new(local,
+                                  &mut callback,
+                                  mir).visit_location(mir, lvalue_use.location)
+        }
+    }
+
+    /// FIXME(pcwalton): This should update the def-use chains.
+    pub fn replace_all_defs_and_uses_with(&self,
+                                          local: Local,
+                                          mir: &mut Mir<'tcx>,
+                                          new_lvalue: Lvalue<'tcx>) {
+        self.mutate_defs_and_uses(local, mir, |lvalue, _, _| *lvalue = new_lvalue.clone())
+    }
+}
+
+struct DefUseFinder<'tcx> {
+    info: IndexVec<Local, Info<'tcx>>,
+}
+
+impl<'tcx> DefUseFinder<'tcx> {
+    fn lvalue_mut_info(&mut self, lvalue: &Lvalue<'tcx>) -> Option<&mut Info<'tcx>> {
+        let info = &mut self.info;
+
+        if let Lvalue::Local(local) = *lvalue {
+            Some(&mut info[local])
+        } else {
+            None
+        }
+    }
+}
+
+impl<'tcx> Visitor<'tcx> for DefUseFinder<'tcx> {
+    fn visit_lvalue(&mut self,
+                    lvalue: &Lvalue<'tcx>,
+                    context: LvalueContext<'tcx>,
+                    location: Location) {
+        if let Some(ref mut info) = self.lvalue_mut_info(lvalue) {
+            info.defs_and_uses.push(Use {
+                context: context,
+                location: location,
+            })
+        }
+        self.super_lvalue(lvalue, context, location)
+    }
+}
+
+impl<'tcx> Info<'tcx> {
+    fn new() -> Info<'tcx> {
+        Info {
+            defs_and_uses: vec![],
+        }
+    }
+
+    pub fn def_count(&self) -> usize {
+        self.defs_and_uses.iter().filter(|lvalue_use| lvalue_use.context.is_mutating_use()).count()
+    }
+
+    pub fn def_count_not_including_drop(&self) -> usize {
+        self.defs_and_uses.iter().filter(|lvalue_use| {
+            lvalue_use.context.is_mutating_use() && !lvalue_use.context.is_drop()
+        }).count()
+    }
+
+    pub fn use_count(&self) -> usize {
+        self.defs_and_uses.iter().filter(|lvalue_use| {
+            lvalue_use.context.is_nonmutating_use()
+        }).count()
+    }
+}
+
+struct MutateUseVisitor<'tcx, F> {
+    query: Local,
+    callback: F,
+    phantom: PhantomData<&'tcx ()>,
+}
+
+impl<'tcx, F> MutateUseVisitor<'tcx, F> {
+    fn new(query: Local, callback: F, _: &Mir<'tcx>)
+           -> MutateUseVisitor<'tcx, F>
+           where F: for<'a> FnMut(&'a mut Lvalue<'tcx>, LvalueContext<'tcx>, Location) {
+        MutateUseVisitor {
+            query: query,
+            callback: callback,
+            phantom: PhantomData,
+        }
+    }
+}
+
+impl<'tcx, F> MutVisitor<'tcx> for MutateUseVisitor<'tcx, F>
+              where F: for<'a> FnMut(&'a mut Lvalue<'tcx>, LvalueContext<'tcx>, Location) {
+    fn visit_lvalue(&mut self,
+                    lvalue: &mut Lvalue<'tcx>,
+                    context: LvalueContext<'tcx>,
+                    location: Location) {
+        if let Lvalue::Local(local) = *lvalue {
+            if local == self.query {
+                (self.callback)(lvalue, context, location)
+            }
+        }
+        self.super_lvalue(lvalue, context, location)
+    }
+}
diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs
new file mode 100644 (file)
index 0000000..ce6deba
--- /dev/null
@@ -0,0 +1,561 @@
+// Copyright 2017 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 std::fmt;
+use rustc::mir::*;
+use rustc::middle::lang_items;
+use rustc::ty::{self, Ty};
+use rustc::ty::subst::{Kind, Subst, Substs};
+use rustc::ty::util::IntTypeExt;
+use rustc_data_structures::indexed_vec::Idx;
+use util::patch::MirPatch;
+
+use std::iter;
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+pub enum DropFlagState {
+    Present, // i.e. initialized
+    Absent, // i.e. deinitialized or "moved"
+}
+
+impl DropFlagState {
+    pub fn value(self) -> bool {
+        match self {
+            DropFlagState::Present => true,
+            DropFlagState::Absent => false
+        }
+    }
+}
+
+#[derive(Debug)]
+pub enum DropStyle {
+    Dead,
+    Static,
+    Conditional,
+    Open,
+}
+
+#[derive(Debug)]
+pub enum DropFlagMode {
+    Shallow,
+    Deep
+}
+
+pub trait DropElaborator<'a, 'tcx: 'a> : fmt::Debug {
+    type Path : Copy + fmt::Debug;
+
+    fn patch(&mut self) -> &mut MirPatch<'tcx>;
+    fn mir(&self) -> &'a Mir<'tcx>;
+    fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx>;
+    fn param_env(&self) -> &'a ty::ParameterEnvironment<'tcx>;
+
+    fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle;
+    fn get_drop_flag(&mut self, path: Self::Path) -> Option<Operand<'tcx>>;
+    fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode);
+
+
+    fn field_subpath(&self, path: Self::Path, field: Field) -> Option<Self::Path>;
+    fn deref_subpath(&self, path: Self::Path) -> Option<Self::Path>;
+    fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option<Self::Path>;
+}
+
+#[derive(Debug)]
+struct DropCtxt<'l, 'b: 'l, 'tcx: 'b, D>
+    where D : DropElaborator<'b, 'tcx> + 'l
+{
+    elaborator: &'l mut D,
+
+    source_info: SourceInfo,
+    is_cleanup: bool,
+
+    lvalue: &'l Lvalue<'tcx>,
+    path: D::Path,
+    succ: BasicBlock,
+    unwind: Option<BasicBlock>,
+}
+
+pub fn elaborate_drop<'b, 'tcx, D>(
+    elaborator: &mut D,
+    source_info: SourceInfo,
+    is_cleanup: bool,
+    lvalue: &Lvalue<'tcx>,
+    path: D::Path,
+    succ: BasicBlock,
+    unwind: Option<BasicBlock>,
+    bb: BasicBlock)
+    where D: DropElaborator<'b, 'tcx>
+{
+    DropCtxt {
+        elaborator, source_info, is_cleanup, lvalue, path, succ, unwind
+    }.elaborate_drop(bb)
+}
+
+impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
+    where D: DropElaborator<'b, 'tcx>
+{
+    fn lvalue_ty(&self, lvalue: &Lvalue<'tcx>) -> Ty<'tcx> {
+        lvalue.ty(self.elaborator.mir(), self.tcx()).to_ty(self.tcx())
+    }
+
+    fn tcx(&self) -> ty::TyCtxt<'b, 'tcx, 'tcx> {
+        self.elaborator.tcx()
+    }
+
+    /// This elaborates a single drop instruction, located at `bb`, and
+    /// patches over it.
+    ///
+    /// The elaborated drop checks the drop flags to only drop what
+    /// is initialized.
+    ///
+    /// In addition, the relevant drop flags also need to be cleared
+    /// to avoid double-drops. However, in the middle of a complex
+    /// drop, one must avoid clearing some of the flags before they
+    /// are read, as that would cause a memory leak.
+    ///
+    /// In particular, when dropping an ADT, multiple fields may be
+    /// joined together under the `rest` subpath. They are all controlled
+    /// by the primary drop flag, but only the last rest-field dropped
+    /// should clear it (and it must also not clear anything else).
+    ///
+    /// FIXME: I think we should just control the flags externally
+    /// and then we do not need this machinery.
+    pub fn elaborate_drop<'a>(&mut self, bb: BasicBlock) {
+        debug!("elaborate_drop({:?})", self);
+        let style = self.elaborator.drop_style(self.path, DropFlagMode::Deep);
+        debug!("elaborate_drop({:?}): live - {:?}", self, style);
+        match style {
+            DropStyle::Dead => {
+                self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto {
+                    target: self.succ
+                });
+            }
+            DropStyle::Static => {
+                let loc = self.terminator_loc(bb);
+                self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep);
+                self.elaborator.patch().patch_terminator(bb, TerminatorKind::Drop {
+                    location: self.lvalue.clone(),
+                    target: self.succ,
+                    unwind: self.unwind
+                });
+            }
+            DropStyle::Conditional => {
+                let drop_bb = self.complete_drop(Some(DropFlagMode::Deep));
+                self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto {
+                    target: drop_bb
+                });
+            }
+            DropStyle::Open => {
+                let drop_bb = self.open_drop();
+                self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto {
+                    target: drop_bb
+                });
+            }
+        }
+    }
+
+    /// Return the lvalue and move path for each field of `variant`,
+    /// (the move path is `None` if the field is a rest field).
+    fn move_paths_for_fields(&self,
+                             base_lv: &Lvalue<'tcx>,
+                             variant_path: D::Path,
+                             variant: &'tcx ty::VariantDef,
+                             substs: &'tcx Substs<'tcx>)
+                             -> Vec<(Lvalue<'tcx>, Option<D::Path>)>
+    {
+        variant.fields.iter().enumerate().map(|(i, f)| {
+            let field = Field::new(i);
+            let subpath = self.elaborator.field_subpath(variant_path, field);
+
+            let field_ty =
+                self.tcx().normalize_associated_type_in_env(
+                    &f.ty(self.tcx(), substs),
+                    self.elaborator.param_env()
+                );
+            (base_lv.clone().field(field, field_ty), subpath)
+        }).collect()
+    }
+
+    fn drop_subpath(&mut self,
+                    is_cleanup: bool,
+                    lvalue: &Lvalue<'tcx>,
+                    path: Option<D::Path>,
+                    succ: BasicBlock,
+                    unwind: Option<BasicBlock>)
+                    -> BasicBlock
+    {
+        if let Some(path) = path {
+            debug!("drop_subpath: for std field {:?}", lvalue);
+
+            DropCtxt {
+                elaborator: self.elaborator,
+                source_info: self.source_info,
+                path, lvalue, succ, unwind, is_cleanup
+            }.elaborated_drop_block()
+        } else {
+            debug!("drop_subpath: for rest field {:?}", lvalue);
+
+            DropCtxt {
+                elaborator: self.elaborator,
+                source_info: self.source_info,
+                lvalue, succ, unwind, is_cleanup,
+                // Using `self.path` here to condition the drop on
+                // our own drop flag.
+                path: self.path
+            }.complete_drop(None)
+        }
+    }
+
+    /// Create one-half of the drop ladder for a list of fields, and return
+    /// the list of steps in it in reverse order.
+    ///
+    /// `unwind_ladder` is such a list of steps in reverse order,
+    /// which is called instead of the next step if the drop unwinds
+    /// (the first field is never reached). If it is `None`, all
+    /// unwind targets are left blank.
+    fn drop_halfladder<'a>(&mut self,
+                           unwind_ladder: Option<Vec<BasicBlock>>,
+                           succ: BasicBlock,
+                           fields: &[(Lvalue<'tcx>, Option<D::Path>)],
+                           is_cleanup: bool)
+                           -> Vec<BasicBlock>
+    {
+        let mut unwind_succ = if is_cleanup {
+            None
+        } else {
+            self.unwind
+        };
+
+        let goto = TerminatorKind::Goto { target: succ };
+        let mut succ = self.new_block(is_cleanup, goto);
+
+        // Always clear the "master" drop flag at the bottom of the
+        // ladder. This is needed because the "master" drop flag
+        // protects the ADT's discriminant, which is invalidated
+        // after the ADT is dropped.
+        let succ_loc = Location { block: succ, statement_index: 0 };
+        self.elaborator.clear_drop_flag(succ_loc, self.path, DropFlagMode::Shallow);
+
+        fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| {
+            succ = self.drop_subpath(is_cleanup, lv, path, succ, unwind_succ);
+            unwind_succ = unwind_ladder.as_ref().map(|p| p[i]);
+            succ
+        }).collect()
+    }
+
+    /// Create a full drop ladder, consisting of 2 connected half-drop-ladders
+    ///
+    /// For example, with 3 fields, the drop ladder is
+    ///
+    /// .d0:
+    ///     ELAB(drop location.0 [target=.d1, unwind=.c1])
+    /// .d1:
+    ///     ELAB(drop location.1 [target=.d2, unwind=.c2])
+    /// .d2:
+    ///     ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`])
+    /// .c1:
+    ///     ELAB(drop location.1 [target=.c2])
+    /// .c2:
+    ///     ELAB(drop location.2 [target=`self.unwind])
+    fn drop_ladder<'a>(&mut self,
+                       fields: Vec<(Lvalue<'tcx>, Option<D::Path>)>)
+                       -> BasicBlock
+    {
+        debug!("drop_ladder({:?}, {:?})", self, fields);
+
+        let mut fields = fields;
+        fields.retain(|&(ref lvalue, _)| {
+            self.tcx().type_needs_drop_given_env(
+                self.lvalue_ty(lvalue), self.elaborator.param_env())
+        });
+
+        debug!("drop_ladder - fields needing drop: {:?}", fields);
+
+        let unwind_ladder = if self.is_cleanup {
+            None
+        } else {
+            let unwind = self.unwind.unwrap(); // FIXME(#6393)
+            Some(self.drop_halfladder(None, unwind, &fields, true))
+        };
+
+        let succ = self.succ; // FIXME(#6393)
+        let is_cleanup = self.is_cleanup;
+        self.drop_halfladder(unwind_ladder, succ, &fields, is_cleanup)
+            .last().cloned().unwrap_or(succ)
+    }
+
+    fn open_drop_for_tuple<'a>(&mut self, tys: &[Ty<'tcx>])
+                               -> BasicBlock
+    {
+        debug!("open_drop_for_tuple({:?}, {:?})", self, tys);
+
+        let fields = tys.iter().enumerate().map(|(i, &ty)| {
+            (self.lvalue.clone().field(Field::new(i), ty),
+             self.elaborator.field_subpath(self.path, Field::new(i)))
+        }).collect();
+
+        self.drop_ladder(fields)
+    }
+
+    fn open_drop_for_box<'a>(&mut self, ty: Ty<'tcx>) -> BasicBlock
+    {
+        debug!("open_drop_for_box({:?}, {:?})", self, ty);
+
+        let interior = self.lvalue.clone().deref();
+        let interior_path = self.elaborator.deref_subpath(self.path);
+
+        let succ = self.succ; // FIXME(#6393)
+        let is_cleanup = self.is_cleanup;
+        let succ = self.box_free_block(ty, succ, is_cleanup);
+        let unwind_succ = self.unwind.map(|u| {
+            self.box_free_block(ty, u, true)
+        });
+
+        self.drop_subpath(is_cleanup, &interior, interior_path, succ, unwind_succ)
+    }
+
+    fn open_drop_for_adt<'a>(&mut self, adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>)
+                             -> BasicBlock {
+        debug!("open_drop_for_adt({:?}, {:?}, {:?})", self, adt, substs);
+
+        match adt.variants.len() {
+            1 => {
+                let fields = self.move_paths_for_fields(
+                    self.lvalue,
+                    self.path,
+                    &adt.variants[0],
+                    substs
+                );
+                self.drop_ladder(fields)
+            }
+            _ => {
+                let mut values = Vec::with_capacity(adt.variants.len());
+                let mut blocks = Vec::with_capacity(adt.variants.len());
+                let mut otherwise = None;
+                for (variant_index, discr) in adt.discriminants(self.tcx()).enumerate() {
+                    let subpath = self.elaborator.downcast_subpath(
+                        self.path, variant_index);
+                    if let Some(variant_path) = subpath {
+                        let base_lv = self.lvalue.clone().elem(
+                            ProjectionElem::Downcast(adt, variant_index)
+                        );
+                        let fields = self.move_paths_for_fields(
+                            &base_lv,
+                            variant_path,
+                            &adt.variants[variant_index],
+                            substs);
+                        values.push(discr);
+                        blocks.push(self.drop_ladder(fields));
+                    } else {
+                        // variant not found - drop the entire enum
+                        if let None = otherwise {
+                            otherwise =
+                                Some(self.complete_drop(Some(DropFlagMode::Shallow)));
+                        }
+                    }
+                }
+                if let Some(block) = otherwise {
+                    blocks.push(block);
+                } else {
+                    values.pop();
+                }
+                // If there are multiple variants, then if something
+                // is present within the enum the discriminant, tracked
+                // by the rest path, must be initialized.
+                //
+                // Additionally, we do not want to switch on the
+                // discriminant after it is free-ed, because that
+                // way lies only trouble.
+                let discr_ty = adt.repr.discr_type().to_ty(self.tcx());
+                let discr = Lvalue::Local(self.new_temp(discr_ty));
+                let discr_rv = Rvalue::Discriminant(self.lvalue.clone());
+                let switch_block = self.elaborator.patch().new_block(BasicBlockData {
+                    statements: vec![
+                        Statement {
+                            source_info: self.source_info,
+                            kind: StatementKind::Assign(discr.clone(), discr_rv),
+                        }
+                    ],
+                    terminator: Some(Terminator {
+                        source_info: self.source_info,
+                        kind: TerminatorKind::SwitchInt {
+                            discr: Operand::Consume(discr),
+                            switch_ty: discr_ty,
+                            values: From::from(values),
+                            targets: blocks,
+                        }
+                    }),
+                    is_cleanup: self.is_cleanup,
+                });
+                self.drop_flag_test_block(switch_block)
+            }
+        }
+    }
+
+    /// The slow-path - create an "open", elaborated drop for a type
+    /// which is moved-out-of only partially, and patch `bb` to a jump
+    /// to it. This must not be called on ADTs with a destructor,
+    /// as these can't be moved-out-of, except for `Box<T>`, which is
+    /// special-cased.
+    ///
+    /// This creates a "drop ladder" that drops the needed fields of the
+    /// ADT, both in the success case or if one of the destructors fail.
+    fn open_drop<'a>(&mut self) -> BasicBlock {
+        let ty = self.lvalue_ty(self.lvalue);
+        match ty.sty {
+            ty::TyClosure(def_id, substs) => {
+                let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
+                self.open_drop_for_tuple(&tys)
+            }
+            ty::TyTuple(tys, _) => {
+                self.open_drop_for_tuple(tys)
+            }
+            ty::TyAdt(def, _) if def.is_box() => {
+                self.open_drop_for_box(ty.boxed_ty())
+            }
+            ty::TyAdt(def, substs) => {
+                self.open_drop_for_adt(def, substs)
+            }
+            _ => bug!("open drop from non-ADT `{:?}`", ty)
+        }
+    }
+
+    /// Return a basic block that drop an lvalue using the context
+    /// and path in `c`. If `mode` is something, also clear `c`
+    /// according to it.
+    ///
+    /// if FLAG(self.path)
+    ///     if let Some(mode) = mode: FLAG(self.path)[mode] = false
+    ///     drop(self.lv)
+    fn complete_drop<'a>(&mut self, drop_mode: Option<DropFlagMode>) -> BasicBlock
+    {
+        debug!("complete_drop({:?},{:?})", self, drop_mode);
+
+        let drop_block = self.drop_block();
+        if let Some(mode) = drop_mode {
+            let block_start = Location { block: drop_block, statement_index: 0 };
+            self.elaborator.clear_drop_flag(block_start, self.path, mode);
+        }
+
+        self.drop_flag_test_block(drop_block)
+    }
+
+    fn elaborated_drop_block<'a>(&mut self) -> BasicBlock {
+        debug!("elaborated_drop_block({:?})", self);
+        let blk = self.drop_block();
+        self.elaborate_drop(blk);
+        blk
+    }
+
+    fn box_free_block<'a>(
+        &mut self,
+        ty: Ty<'tcx>,
+        target: BasicBlock,
+        is_cleanup: bool
+    ) -> BasicBlock {
+        let block = self.unelaborated_free_block(ty, target, is_cleanup);
+        self.drop_flag_test_block_with_succ(is_cleanup, block, target)
+    }
+
+    fn unelaborated_free_block<'a>(
+        &mut self,
+        ty: Ty<'tcx>,
+        target: BasicBlock,
+        is_cleanup: bool
+    ) -> BasicBlock {
+        let tcx = self.tcx();
+        let unit_temp = Lvalue::Local(self.new_temp(tcx.mk_nil()));
+        let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem);
+        let substs = tcx.mk_substs(iter::once(Kind::from(ty)));
+        let fty = tcx.item_type(free_func).subst(tcx, substs);
+
+        let free_block = self.elaborator.patch().new_block(BasicBlockData {
+            statements: vec![],
+            terminator: Some(Terminator {
+                source_info: self.source_info, kind: TerminatorKind::Call {
+                    func: Operand::Constant(Constant {
+                        span: self.source_info.span,
+                        ty: fty,
+                        literal: Literal::Item {
+                            def_id: free_func,
+                            substs: substs
+                        }
+                    }),
+                    args: vec![Operand::Consume(self.lvalue.clone())],
+                    destination: Some((unit_temp, target)),
+                    cleanup: None
+                }
+            }),
+            is_cleanup: is_cleanup
+        });
+        let block_start = Location { block: free_block, statement_index: 0 };
+        self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow);
+        free_block
+    }
+
+    fn drop_block<'a>(&mut self) -> BasicBlock {
+        let block = TerminatorKind::Drop {
+            location: self.lvalue.clone(),
+            target: self.succ,
+            unwind: self.unwind
+        };
+        let is_cleanup = self.is_cleanup; // FIXME(#6393)
+        self.new_block(is_cleanup, block)
+    }
+
+    fn drop_flag_test_block<'a>(&mut self, on_set: BasicBlock) -> BasicBlock {
+        let is_cleanup = self.is_cleanup;
+        let succ = self.succ; // FIXME(#6393)
+        self.drop_flag_test_block_with_succ(is_cleanup, on_set, succ)
+    }
+
+    fn drop_flag_test_block_with_succ<'a>(&mut self,
+                                          is_cleanup: bool,
+                                          on_set: BasicBlock,
+                                          on_unset: BasicBlock)
+                                          -> BasicBlock
+    {
+        let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow);
+        debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}",
+               self, is_cleanup, on_set, style);
+
+        match style {
+            DropStyle::Dead => on_unset,
+            DropStyle::Static => on_set,
+            DropStyle::Conditional | DropStyle::Open => {
+                let flag = self.elaborator.get_drop_flag(self.path).unwrap();
+                let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset);
+                self.new_block(is_cleanup, term)
+            }
+        }
+    }
+
+    fn new_block<'a>(&mut self,
+                     is_cleanup: bool,
+                     k: TerminatorKind<'tcx>)
+                     -> BasicBlock
+    {
+        self.elaborator.patch().new_block(BasicBlockData {
+            statements: vec![],
+            terminator: Some(Terminator {
+                source_info: self.source_info, kind: k
+            }),
+            is_cleanup: is_cleanup
+        })
+    }
+
+    fn new_temp(&mut self, ty: Ty<'tcx>) -> Local {
+        self.elaborator.patch().new_temp(ty)
+    }
+
+    fn terminator_loc(&mut self, bb: BasicBlock) -> Location {
+        let mir = self.elaborator.mir();
+        self.elaborator.patch().terminator_loc(mir, bb)
+    }
+}
diff --git a/src/librustc_mir/util/graphviz.rs b/src/librustc_mir/util/graphviz.rs
new file mode 100644 (file)
index 0000000..91600b9
--- /dev/null
@@ -0,0 +1,173 @@
+// Copyright 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 dot;
+use rustc::hir::def_id::DefId;
+use rustc::mir::*;
+use rustc::ty::TyCtxt;
+use std::fmt::Debug;
+use std::io::{self, Write};
+use syntax::ast::NodeId;
+
+use rustc_data_structures::indexed_vec::Idx;
+
+/// Write a graphviz DOT graph of a list of MIRs.
+pub fn write_mir_graphviz<'a, 'b, 'tcx, W, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
+                                              iter: I,
+                                              w: &mut W)
+                                              -> io::Result<()>
+    where W: Write, I: Iterator<Item=DefId>
+{
+    for def_id in iter {
+        let nodeid = tcx.hir.as_local_node_id(def_id).unwrap();
+        let mir = &tcx.item_mir(def_id);
+
+        writeln!(w, "digraph Mir_{} {{", nodeid)?;
+
+        // Global graph properties
+        writeln!(w, r#"    graph [fontname="monospace"];"#)?;
+        writeln!(w, r#"    node [fontname="monospace"];"#)?;
+        writeln!(w, r#"    edge [fontname="monospace"];"#)?;
+
+        // Graph label
+        write_graph_label(tcx, nodeid, mir, w)?;
+
+        // Nodes
+        for (block, _) in mir.basic_blocks().iter_enumerated() {
+            write_node(block, mir, w)?;
+        }
+
+        // Edges
+        for (source, _) in mir.basic_blocks().iter_enumerated() {
+            write_edges(source, mir, w)?;
+        }
+        writeln!(w, "}}")?
+    }
+    Ok(())
+}
+
+/// Write a graphviz HTML-styled label for the given basic block, with
+/// all necessary escaping already performed. (This is suitable for
+/// emitting directly, as is done in this module, or for use with the
+/// LabelText::HtmlStr from libgraphviz.)
+///
+/// `init` and `fini` are callbacks for emitting additional rows of
+/// data (using HTML enclosed with `<tr>` in the emitted text).
+pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
+                                              mir: &Mir,
+                                              w: &mut W,
+                                              num_cols: u32,
+                                              init: INIT,
+                                              fini: FINI) -> io::Result<()>
+    where INIT: Fn(&mut W) -> io::Result<()>,
+          FINI: Fn(&mut W) -> io::Result<()>
+{
+    let data = &mir[block];
+
+    write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
+
+    // Basic block number at the top.
+    write!(w, r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#,
+           attrs=r#"bgcolor="gray" align="center""#,
+           colspan=num_cols,
+           blk=block.index())?;
+
+    init(w)?;
+
+    // List of statements in the middle.
+    if !data.statements.is_empty() {
+        write!(w, r#"<tr><td align="left" balign="left">"#)?;
+        for statement in &data.statements {
+            write!(w, "{}<br/>", escape(statement))?;
+        }
+        write!(w, "</td></tr>")?;
+    }
+
+    // Terminator head at the bottom, not including the list of successor blocks. Those will be
+    // displayed as labels on the edges between blocks.
+    let mut terminator_head = String::new();
+    data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
+    write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
+
+    fini(w)?;
+
+    // Close the table
+    writeln!(w, "</table>")
+}
+
+/// Write a graphviz DOT node for the given basic block.
+fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
+    // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
+    write!(w, r#"    {} [shape="none", label=<"#, node(block))?;
+    write_node_label(block, mir, w, 1, |_| Ok(()), |_| Ok(()))?;
+    // Close the node label and the node itself.
+    writeln!(w, ">];")
+}
+
+/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
+fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
+    let terminator = mir[source].terminator();
+    let labels = terminator.kind.fmt_successor_labels();
+
+    for (&target, label) in terminator.successors().iter().zip(labels) {
+        writeln!(w, r#"    {} -> {} [label="{}"];"#, node(source), node(target), label)?;
+    }
+
+    Ok(())
+}
+
+/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
+/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
+/// all the variables and temporaries.
+fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                         nid: NodeId,
+                                         mir: &Mir,
+                                         w: &mut W)
+                                         -> io::Result<()> {
+    write!(w, "    label=<fn {}(", dot::escape_html(&tcx.node_path_str(nid)))?;
+
+    // fn argument types.
+    for (i, arg) in mir.args_iter().enumerate() {
+        if i > 0 {
+            write!(w, ", ")?;
+        }
+        write!(w, "{:?}: {}", Lvalue::Local(arg), escape(&mir.local_decls[arg].ty))?;
+    }
+
+    write!(w, ") -&gt; {}", escape(mir.return_ty))?;
+    write!(w, r#"<br align="left"/>"#)?;
+
+    for local in mir.vars_and_temps_iter() {
+        let decl = &mir.local_decls[local];
+
+        write!(w, "let ")?;
+        if decl.mutability == Mutability::Mut {
+            write!(w, "mut ")?;
+        }
+
+        if let Some(name) = decl.name {
+            write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
+                   Lvalue::Local(local), escape(&decl.ty), name)?;
+        } else {
+            write!(w, r#"let mut {:?}: {};<br align="left"/>"#,
+                   Lvalue::Local(local), escape(&decl.ty))?;
+        }
+    }
+
+    writeln!(w, ">;")
+}
+
+fn node(block: BasicBlock) -> String {
+    format!("bb{}", block.index())
+}
+
+fn escape<T: Debug>(t: &T) -> String {
+    dot::escape_html(&format!("{:?}", t))
+}
diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs
new file mode 100644 (file)
index 0000000..cafc5bc
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2017 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.
+
+pub mod elaborate_drops;
+pub mod def_use;
+pub mod patch;
+
+mod graphviz;
+mod pretty;
+
+pub use self::pretty::{dump_mir, write_mir_pretty};
+pub use self::graphviz::{write_mir_graphviz};
+pub use self::graphviz::write_node_label as write_graphviz_node_label;
diff --git a/src/librustc_mir/util/patch.rs b/src/librustc_mir/util/patch.rs
new file mode 100644 (file)
index 0000000..19f240d
--- /dev/null
@@ -0,0 +1,178 @@
+// Copyright 2016 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 rustc::ty::Ty;
+use rustc::mir::*;
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+
+/// This struct represents a patch to MIR, which can add
+/// new statements and basic blocks and patch over block
+/// terminators.
+pub struct MirPatch<'tcx> {
+    patch_map: IndexVec<BasicBlock, Option<TerminatorKind<'tcx>>>,
+    new_blocks: Vec<BasicBlockData<'tcx>>,
+    new_statements: Vec<(Location, StatementKind<'tcx>)>,
+    new_locals: Vec<LocalDecl<'tcx>>,
+    resume_block: BasicBlock,
+    next_local: usize,
+}
+
+impl<'tcx> MirPatch<'tcx> {
+    pub fn new(mir: &Mir<'tcx>) -> Self {
+        let mut result = MirPatch {
+            patch_map: IndexVec::from_elem(None, mir.basic_blocks()),
+            new_blocks: vec![],
+            new_statements: vec![],
+            new_locals: vec![],
+            next_local: mir.local_decls.len(),
+            resume_block: START_BLOCK
+        };
+
+        // make sure the MIR we create has a resume block. It is
+        // completely legal to convert jumps to the resume block
+        // to jumps to None, but we occasionally have to add
+        // instructions just before that.
+
+        let mut resume_block = None;
+        let mut resume_stmt_block = None;
+        for (bb, block) in mir.basic_blocks().iter_enumerated() {
+            if let TerminatorKind::Resume = block.terminator().kind {
+                if block.statements.len() > 0 {
+                    resume_stmt_block = Some(bb);
+                } else {
+                    resume_block = Some(bb);
+                }
+                break
+            }
+        }
+        let resume_block = resume_block.unwrap_or_else(|| {
+            result.new_block(BasicBlockData {
+                statements: vec![],
+                terminator: Some(Terminator {
+                    source_info: SourceInfo {
+                        span: mir.span,
+                        scope: ARGUMENT_VISIBILITY_SCOPE
+                    },
+                    kind: TerminatorKind::Resume
+                }),
+                is_cleanup: true
+            })});
+        result.resume_block = resume_block;
+        if let Some(resume_stmt_block) = resume_stmt_block {
+            result.patch_terminator(resume_stmt_block, TerminatorKind::Goto {
+                target: resume_block
+            });
+        }
+        result
+    }
+
+    pub fn resume_block(&self) -> BasicBlock {
+        self.resume_block
+    }
+
+    pub fn is_patched(&self, bb: BasicBlock) -> bool {
+        self.patch_map[bb].is_some()
+    }
+
+    pub fn terminator_loc(&self, mir: &Mir<'tcx>, bb: BasicBlock) -> Location {
+        let offset = match bb.index().checked_sub(mir.basic_blocks().len()) {
+            Some(index) => self.new_blocks[index].statements.len(),
+            None => mir[bb].statements.len()
+        };
+        Location {
+            block: bb,
+            statement_index: offset
+        }
+    }
+
+    pub fn new_temp(&mut self, ty: Ty<'tcx>) -> Local {
+        let index = self.next_local;
+        self.next_local += 1;
+        self.new_locals.push(LocalDecl::new_temp(ty));
+        Local::new(index as usize)
+    }
+
+    pub fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {
+        let block = BasicBlock::new(self.patch_map.len());
+        debug!("MirPatch: new_block: {:?}: {:?}", block, data);
+        self.new_blocks.push(data);
+        self.patch_map.push(None);
+        block
+    }
+
+    pub fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) {
+        assert!(self.patch_map[block].is_none());
+        debug!("MirPatch: patch_terminator({:?}, {:?})", block, new);
+        self.patch_map[block] = Some(new);
+    }
+
+    pub fn add_statement(&mut self, loc: Location, stmt: StatementKind<'tcx>) {
+        debug!("MirPatch: add_statement({:?}, {:?})", loc, stmt);
+        self.new_statements.push((loc, stmt));
+    }
+
+    pub fn add_assign(&mut self, loc: Location, lv: Lvalue<'tcx>, rv: Rvalue<'tcx>) {
+        self.add_statement(loc, StatementKind::Assign(lv, rv));
+    }
+
+    pub fn apply(self, mir: &mut Mir<'tcx>) {
+        debug!("MirPatch: {:?} new temps, starting from index {}: {:?}",
+               self.new_locals.len(), mir.local_decls.len(), self.new_locals);
+        debug!("MirPatch: {} new blocks, starting from index {}",
+               self.new_blocks.len(), mir.basic_blocks().len());
+        mir.basic_blocks_mut().extend(self.new_blocks);
+        mir.local_decls.extend(self.new_locals);
+        for (src, patch) in self.patch_map.into_iter_enumerated() {
+            if let Some(patch) = patch {
+                debug!("MirPatch: patching block {:?}", src);
+                mir[src].terminator_mut().kind = patch;
+            }
+        }
+
+        let mut new_statements = self.new_statements;
+        new_statements.sort_by(|u,v| u.0.cmp(&v.0));
+
+        let mut delta = 0;
+        let mut last_bb = START_BLOCK;
+        for (mut loc, stmt) in new_statements {
+            if loc.block != last_bb {
+                delta = 0;
+                last_bb = loc.block;
+            }
+            debug!("MirPatch: adding statement {:?} at loc {:?}+{}",
+                   stmt, loc, delta);
+            loc.statement_index += delta;
+            let source_info = Self::source_info_for_index(
+                &mir[loc.block], loc
+            );
+            mir[loc.block].statements.insert(
+                loc.statement_index, Statement {
+                    source_info: source_info,
+                    kind: stmt
+                });
+            delta += 1;
+        }
+    }
+
+    pub fn source_info_for_index(data: &BasicBlockData, loc: Location) -> SourceInfo {
+        match data.statements.get(loc.statement_index) {
+            Some(stmt) => stmt.source_info,
+            None => data.terminator().source_info
+        }
+    }
+
+    pub fn source_info_for_location(&self, mir: &Mir, loc: Location) -> SourceInfo {
+        let data = match loc.block.index().checked_sub(mir.basic_blocks().len()) {
+            Some(new) => &self.new_blocks[new],
+            None => &mir[loc.block]
+        };
+        Self::source_info_for_index(data, loc)
+    }
+}
diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs
new file mode 100644 (file)
index 0000000..35734dc
--- /dev/null
@@ -0,0 +1,311 @@
+// Copyright 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 rustc::hir;
+use rustc::hir::def_id::DefId;
+use rustc::mir::*;
+use rustc::mir::transform::MirSource;
+use rustc::ty::TyCtxt;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::indexed_vec::{Idx};
+use std::fmt::Display;
+use std::fs;
+use std::io::{self, Write};
+use std::path::{PathBuf, Path};
+
+const INDENT: &'static str = "    ";
+/// Alignment for lining up comments following MIR statements
+const ALIGN: usize = 40;
+
+/// If the session is properly configured, dumps a human-readable
+/// representation of the mir into:
+///
+/// ```text
+/// rustc.node<node_id>.<pass_name>.<disambiguator>
+/// ```
+///
+/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
+/// where `<filter>` takes the following forms:
+///
+/// - `all` -- dump MIR for all fns, all passes, all everything
+/// - `substring1&substring2,...` -- `&`-separated list of substrings
+///   that can appear in the pass-name or the `item_path_str` for the given
+///   node-id. If any one of the substrings match, the data is dumped out.
+pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                          pass_name: &str,
+                          disambiguator: &Display,
+                          src: MirSource,
+                          mir: &Mir<'tcx>) {
+    let filters = match tcx.sess.opts.debugging_opts.dump_mir {
+        None => return,
+        Some(ref filters) => filters,
+    };
+    let node_id = src.item_id();
+    let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id));
+    let is_matched =
+        filters.split("&")
+               .any(|filter| {
+                   filter == "all" ||
+                       pass_name.contains(filter) ||
+                       node_path.contains(filter)
+               });
+    if !is_matched {
+        return;
+    }
+
+    let promotion_id = match src {
+        MirSource::Promoted(_, id) => format!("-{:?}", id),
+        _ => String::new()
+    };
+
+    let mut file_path = PathBuf::new();
+    if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
+        let p = Path::new(file_dir);
+        file_path.push(p);
+    };
+    let file_name = format!("rustc.node{}{}.{}.{}.mir",
+                            node_id, promotion_id, pass_name, disambiguator);
+    file_path.push(&file_name);
+    let _ = fs::File::create(&file_path).and_then(|mut file| {
+        writeln!(file, "// MIR for `{}`", node_path)?;
+        writeln!(file, "// node_id = {}", node_id)?;
+        writeln!(file, "// pass_name = {}", pass_name)?;
+        writeln!(file, "// disambiguator = {}", disambiguator)?;
+        writeln!(file, "")?;
+        write_mir_fn(tcx, src, mir, &mut file)?;
+        Ok(())
+    });
+}
+
+/// Write out a human-readable textual representation for the given MIR.
+pub fn write_mir_pretty<'a, 'b, 'tcx, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
+                                         iter: I,
+                                         w: &mut Write)
+                                         -> io::Result<()>
+    where I: Iterator<Item=DefId>, 'tcx: 'a
+{
+    let mut first = true;
+    for def_id in iter.filter(DefId::is_local) {
+        let mir = &tcx.item_mir(def_id);
+
+        if first {
+            first = false;
+        } else {
+            // Put empty lines between all items
+            writeln!(w, "")?;
+        }
+
+        let id = tcx.hir.as_local_node_id(def_id).unwrap();
+        let src = MirSource::from_node(tcx, id);
+        write_mir_fn(tcx, src, mir, w)?;
+
+        for (i, mir) in mir.promoted.iter_enumerated() {
+            writeln!(w, "")?;
+            write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w)?;
+        }
+    }
+    Ok(())
+}
+
+pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                              src: MirSource,
+                              mir: &Mir<'tcx>,
+                              w: &mut Write)
+                              -> io::Result<()> {
+    write_mir_intro(tcx, src, mir, w)?;
+    for block in mir.basic_blocks().indices() {
+        write_basic_block(tcx, block, mir, w)?;
+        if block.index() + 1 != mir.basic_blocks().len() {
+            writeln!(w, "")?;
+        }
+    }
+
+    writeln!(w, "}}")?;
+    Ok(())
+}
+
+/// Write out a human-readable textual representation for the given basic block.
+fn write_basic_block(tcx: TyCtxt,
+                     block: BasicBlock,
+                     mir: &Mir,
+                     w: &mut Write)
+                     -> io::Result<()> {
+    let data = &mir[block];
+
+    // Basic block label at the top.
+    writeln!(w, "{}{:?}: {{", INDENT, block)?;
+
+    // List of statements in the middle.
+    let mut current_location = Location { block: block, statement_index: 0 };
+    for statement in &data.statements {
+        let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
+        writeln!(w, "{0:1$} // {2}",
+                 indented_mir,
+                 ALIGN,
+                 comment(tcx, statement.source_info))?;
+
+        current_location.statement_index += 1;
+    }
+
+    // Terminator at the bottom.
+    let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
+    writeln!(w, "{0:1$} // {2}",
+             indented_terminator,
+             ALIGN,
+             comment(tcx, data.terminator().source_info))?;
+
+    writeln!(w, "{}}}", INDENT)
+}
+
+fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
+    format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
+}
+
+/// Prints user-defined variables in a scope tree.
+///
+/// Returns the total number of variables printed.
+fn write_scope_tree(tcx: TyCtxt,
+                    mir: &Mir,
+                    scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
+                    w: &mut Write,
+                    parent: VisibilityScope,
+                    depth: usize)
+                    -> io::Result<()> {
+    let indent = depth * INDENT.len();
+
+    let children = match scope_tree.get(&parent) {
+        Some(childs) => childs,
+        None => return Ok(()),
+    };
+
+    for &child in children {
+        let data = &mir.visibility_scopes[child];
+        assert_eq!(data.parent_scope, Some(parent));
+        writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
+
+        // User variable types (including the user's name in a comment).
+        for local in mir.vars_iter() {
+            let var = &mir.local_decls[local];
+            let (name, source_info) = if var.source_info.unwrap().scope == child {
+                (var.name.unwrap(), var.source_info.unwrap())
+            } else {
+                // Not a variable or not declared in this scope.
+                continue;
+            };
+
+            let mut_str = if var.mutability == Mutability::Mut {
+                "mut "
+            } else {
+                ""
+            };
+
+            let indent = indent + INDENT.len();
+            let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
+                                       INDENT,
+                                       indent,
+                                       mut_str,
+                                       local,
+                                       var.ty);
+            writeln!(w, "{0:1$} // \"{2}\" in {3}",
+                     indented_var,
+                     ALIGN,
+                     name,
+                     comment(tcx, source_info))?;
+        }
+
+        write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
+
+        writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
+    }
+
+    Ok(())
+}
+
+/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
+/// local variables (both user-defined bindings and compiler temporaries).
+fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                             src: MirSource,
+                             mir: &Mir,
+                             w: &mut Write)
+                             -> io::Result<()> {
+    write_mir_sig(tcx, src, mir, w)?;
+    writeln!(w, " {{")?;
+
+    // construct a scope tree and write it out
+    let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
+    for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
+        if let Some(parent) = scope_data.parent_scope {
+            scope_tree.entry(parent)
+                      .or_insert(vec![])
+                      .push(VisibilityScope::new(index));
+        } else {
+            // Only the argument scope has no parent, because it's the root.
+            assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
+        }
+    }
+
+    // Print return pointer
+    let indented_retptr = format!("{}let mut {:?}: {};",
+                                  INDENT,
+                                  RETURN_POINTER,
+                                  mir.return_ty);
+    writeln!(w, "{0:1$} // return pointer",
+             indented_retptr,
+             ALIGN)?;
+
+    write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
+
+    write_temp_decls(mir, w)?;
+
+    // Add an empty line before the first block is printed.
+    writeln!(w, "")?;
+
+    Ok(())
+}
+
+fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
+                 -> io::Result<()>
+{
+    match src {
+        MirSource::Fn(_) => write!(w, "fn")?,
+        MirSource::Const(_) => write!(w, "const")?,
+        MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
+        MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
+        MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
+    }
+
+    write!(w, " {}", tcx.node_path_str(src.item_id()))?;
+
+    if let MirSource::Fn(_) = src {
+        write!(w, "(")?;
+
+        // fn argument types.
+        for (i, arg) in mir.args_iter().enumerate() {
+            if i != 0 {
+                write!(w, ", ")?;
+            }
+            write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
+        }
+
+        write!(w, ") -> {}", mir.return_ty)
+    } else {
+        assert_eq!(mir.arg_count, 0);
+        write!(w, ": {} =", mir.return_ty)
+    }
+}
+
+fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
+    // Compiler-introduced temporary types.
+    for temp in mir.temps_iter() {
+        writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
+    }
+
+    Ok(())
+}