]> git.lizzy.rs Git - rust.git/commitdiff
extend liveness to distinguish "drop" and "non-drop" uses
authorNiko Matsakis <niko@alum.mit.edu>
Tue, 24 Oct 2017 20:20:47 +0000 (16:20 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Tue, 31 Oct 2017 16:41:38 +0000 (12:41 -0400)
src/librustc_mir/transform/generator.rs
src/librustc_mir/transform/nll/constraint_generation.rs
src/librustc_mir/transform/nll/mod.rs
src/librustc_mir/util/liveness.rs
src/test/mir-opt/nll/liveness-call-subtlety.rs
src/test/mir-opt/nll/liveness-drop-intra-block.rs
src/test/mir-opt/nll/liveness-interblock.rs
src/test/mir-opt/nll/region-liveness-basic.rs
src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs [new file with mode: 0644]

index 7d0814b67fba5b1e216d236b62a75466c0e792af..52a50333f4577d0d9d6ad1cf663b16f676a7bc96 100644 (file)
@@ -68,7 +68,7 @@
 use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior};
 use rustc::ty::subst::{Kind, Substs};
 use util::dump_mir;
-use util::liveness;
+use util::liveness::{self, LivenessMode};
 use rustc_const_math::ConstInt;
 use rustc_data_structures::indexed_vec::Idx;
 use rustc_data_structures::indexed_set::IdxSetBuf;
@@ -348,7 +348,10 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     ignored.visit_mir(mir);
 
     let mut set = liveness::LocalSet::new_empty(mir.local_decls.len());
-    let liveness = liveness::liveness_of_locals(mir);
+    let liveness = liveness::liveness_of_locals(mir, LivenessMode {
+        include_regular_use: true,
+        include_drops: true,
+    });
     liveness::dump_mir(tcx, "generator_liveness", source, mir, &liveness);
 
     let mut storage_liveness_map = HashMap::new();
index 1c0c3b6fc33f356d5c3493f1fb457a6b01263b82..518e140b5dd1b0530a8ddcb2cd5d0f4119d6a2ae 100644 (file)
 
 use rustc::mir::Mir;
 use rustc::infer::InferCtxt;
-use util::liveness::LivenessResult;
 
+use super::LivenessResults;
 use super::ToRegionIndex;
 use super::region_infer::RegionInferenceContext;
 
-pub fn generate_constraints<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-                                            regioncx: &mut RegionInferenceContext,
-                                            mir: &Mir<'tcx>,
-                                            liveness: &LivenessResult)
-{
-    ConstraintGeneration { infcx, regioncx, mir, liveness }.add_constraints();
+pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    regioncx: &mut RegionInferenceContext,
+    mir: &Mir<'tcx>,
+    liveness: &LivenessResults,
+) {
+    ConstraintGeneration {
+        infcx,
+        regioncx,
+        mir,
+        liveness,
+    }.add_constraints();
 }
 
 struct ConstraintGeneration<'constrain, 'gcx: 'tcx, 'tcx: 'constrain> {
     infcx: &'constrain InferCtxt<'constrain, 'gcx, 'tcx>,
     regioncx: &'constrain mut RegionInferenceContext,
     mir: &'constrain Mir<'tcx>,
-    liveness: &'constrain LivenessResult,
+    liveness: &'constrain LivenessResults,
 }
 
 impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> {
@@ -47,18 +53,23 @@ fn add_liveness_constraints(&mut self) {
         for bb in self.mir.basic_blocks().indices() {
             debug!("add_liveness_constraints: bb={:?}", bb);
 
-            self.liveness.simulate_block(self.mir, bb, |location, live_locals| {
-                debug!("add_liveness_constraints: location={:?} live_locals={:?}",
-                       location, live_locals);
+            self.liveness
+                .regular
+                .simulate_block(self.mir, bb, |location, live_locals| {
+                    debug!(
+                        "add_liveness_constraints: location={:?} live_locals={:?}",
+                        location,
+                        live_locals
+                    );
 
-                for live_local in live_locals.iter() {
-                    let live_local_ty = self.mir.local_decls[live_local].ty;
-                    tcx.for_each_free_region(&live_local_ty, |live_region| {
-                        let vid = live_region.to_region_index();
-                        self.regioncx.add_live_point(vid, location);
-                    })
-                }
-            });
+                    for live_local in live_locals.iter() {
+                        let live_local_ty = self.mir.local_decls[live_local].ty;
+                        tcx.for_each_free_region(&live_local_ty, |live_region| {
+                            let vid = live_region.to_region_index();
+                            self.regioncx.add_live_point(vid, location);
+                        })
+                    }
+                });
         }
     }
 }
index 273972f693799c93674e302d36490fb042240e0c..131f088d91c7534c44e01363b4c0cc1d0716e2d2 100644 (file)
@@ -16,7 +16,7 @@
 use rustc_data_structures::indexed_vec::Idx;
 use std::collections::BTreeSet;
 use std::fmt;
-use util::liveness::{self, LivenessResult};
+use util::liveness::{self, LivenessResult, LivenessMode};
 
 use util as mir_util;
 use self::mir_util::PassWhere;
@@ -50,7 +50,17 @@ fn run_pass<'a, 'tcx>(
             let num_region_variables = renumber::renumber_mir(infcx, mir);
 
             // Compute what is live where.
-            let liveness = &liveness::liveness_of_locals(mir);
+            let liveness = &LivenessResults {
+                regular: liveness::liveness_of_locals(mir, LivenessMode {
+                    include_regular_use: true,
+                    include_drops: false,
+                }),
+
+                drop: liveness::liveness_of_locals(mir, LivenessMode {
+                    include_regular_use: false,
+                    include_drops: true,
+                })
+            };
 
             // Create the region inference context, generate the constraints,
             // and then solve them.
@@ -64,9 +74,14 @@ fn run_pass<'a, 'tcx>(
     }
 }
 
+struct LivenessResults {
+    regular: LivenessResult,
+    drop: LivenessResult,
+}
+
 fn dump_mir_results<'a, 'gcx, 'tcx>(
     infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-    liveness: &LivenessResult,
+    liveness: &LivenessResults,
     source: MirSource,
     regioncx: &RegionInferenceContext,
     mir: &Mir<'tcx>,
@@ -75,11 +90,22 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
         return;
     }
 
-    let liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
+    let regular_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
+        .indices()
+        .flat_map(|bb| {
+            let mut results = vec![];
+            liveness.regular.simulate_block(&mir, bb, |location, local_set| {
+                results.push((location, local_set.clone()));
+            });
+            results
+        })
+        .collect();
+
+    let drop_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
         .indices()
         .flat_map(|bb| {
             let mut results = vec![];
-            liveness.simulate_block(&mir, bb, |location, local_set| {
+            liveness.drop.simulate_block(&mir, bb, |location, local_set| {
                 results.push((location, local_set.clone()));
             });
             results
@@ -96,16 +122,17 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
             // Before each basic block, dump out the values
             // that are live on entry to the basic block.
             PassWhere::BeforeBlock(bb) => {
-                let local_set = &liveness.ins[bb];
-                writeln!(out, "    | Variables live on entry to the block {:?}:", bb)?;
-                for local in local_set.iter() {
-                    writeln!(out, "    | - {:?}", local)?;
-                }
+                writeln!(out, "    | Variables regular-live on entry to the block {:?}: {:?}",
+                         bb, liveness.regular.ins[bb])?;
+                writeln!(out, "    | Variables drop-live on entry to the block {:?}: {:?}",
+                         bb, liveness.drop.ins[bb])?;
             }
 
             PassWhere::InCFG(location) => {
-                let local_set = &liveness_per_location[&location];
-                writeln!(out, "        | Live variables here: {:?}", local_set)?;
+                let local_set = &regular_liveness_per_location[&location];
+                writeln!(out, "        | Regular-Live variables here: {:?}", local_set)?;
+                let local_set = &drop_liveness_per_location[&location];
+                writeln!(out, "        | Drop-Live variables here: {:?}", local_set)?;
             }
 
             PassWhere::AfterCFG => {}
index 514ff2ab830caed227ae3ea78bac2aa7db1ca7c3..7658e49ea5ebd26666019a557de03ddb0c67acd8 100644 (file)
 
 use rustc::mir::*;
 use rustc::mir::visit::{LvalueContext, Visitor};
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use rustc_data_structures::indexed_set::IdxSetBuf;
-use util::pretty::{write_basic_block, dump_enabled, write_mir_intro};
+use util::pretty::{dump_enabled, write_basic_block, write_mir_intro};
 use rustc::mir::transform::MirSource;
 use rustc::ty::item_path;
-use std::path::{PathBuf, Path};
+use std::path::{Path, PathBuf};
 use std::fs;
 use rustc::ty::TyCtxt;
 use std::io::{self, Write};
 
 pub type LocalSet = IdxSetBuf<Local>;
 
-// This gives the result of the liveness analysis at the boundary of basic blocks
+/// This gives the result of the liveness analysis at the boundary of
+/// basic blocks. You can use `simulate_block` to obtain the
+/// intra-block results.
 pub struct LivenessResult {
+    /// Liveness mode in use when these results were computed.
+    pub mode: LivenessMode,
+
+    /// Live variables on entry to each basic block.
     pub ins: IndexVec<BasicBlock, LocalSet>,
+
+    /// Live variables on exit to each basic block. This is equal to
+    /// the union of the `ins` for each successor.
     pub outs: IndexVec<BasicBlock, LocalSet>,
 }
 
-pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult {
+#[derive(Copy, Clone, Debug)]
+pub struct LivenessMode {
+    /// If true, then we will consider "regular uses" of a variable to be live.
+    /// For example, if the user writes `foo(x)`, then this is a regular use of
+    /// the variable `x`.
+    pub include_regular_use: bool,
+
+    /// If true, then we will consider (implicit) drops of a variable
+    /// to be live.  For example, if the user writes `{ let x =
+    /// vec![...]; .. }`, then the drop at the end of the block is an
+    /// implicit drop.
+    ///
+    /// NB. Despite its name, a call like `::std::mem::drop(x)` is
+    /// **not** considered a drop for this purposes, but rather a
+    /// regular use.
+    pub include_drops: bool,
+}
+
+/// Compute which local variables are live within the given function
+/// `mir`. The liveness mode `mode` determines what sorts of uses are
+/// considered to make a variable live (e.g., do drops count?).
+pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>, mode: LivenessMode) -> LivenessResult {
     let locals = mir.local_decls.len();
-    let def_use: IndexVec<_, _> = mir.basic_blocks().iter().map(|b| {
-        block(b, locals)
-    }).collect();
+    let def_use: IndexVec<_, _> = mir.basic_blocks()
+        .iter()
+        .map(|b| block(mode, b, locals))
+        .collect();
 
     let mut ins: IndexVec<_, _> = mir.basic_blocks()
         .indices()
@@ -89,10 +120,7 @@ pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult {
         }
     }
 
-    LivenessResult {
-        ins,
-        outs,
-    }
+    LivenessResult { mode, ins, outs }
 }
 
 impl LivenessResult {
@@ -100,11 +128,9 @@ impl LivenessResult {
     /// basic block `block`.  At each point within `block`, invokes
     /// the callback `op` with the current location and the set of
     /// variables that are live on entry to that location.
-    pub fn simulate_block<'tcx, OP>(&self,
-                                    mir: &Mir<'tcx>,
-                                    block: BasicBlock,
-                                    mut callback: OP)
-        where OP: FnMut(Location, &LocalSet)
+    pub fn simulate_block<'tcx, OP>(&self, mir: &Mir<'tcx>, block: BasicBlock, mut callback: OP)
+    where
+        OP: FnMut(Location, &LocalSet),
     {
         let data = &mir[block];
 
@@ -116,7 +142,10 @@ pub fn simulate_block<'tcx, OP>(&self,
         let mut statement_index = data.statements.len();
 
         // Compute liveness right before terminator and invoke callback.
-        let terminator_location = Location { block, statement_index };
+        let terminator_location = Location {
+            block,
+            statement_index,
+        };
         let terminator_defs_uses = self.defs_uses(mir, terminator_location, &data.terminator);
         terminator_defs_uses.apply(&mut bits);
         callback(terminator_location, &bits);
@@ -124,7 +153,10 @@ pub fn simulate_block<'tcx, OP>(&self,
         // Compute liveness before each statement (in rev order) and invoke callback.
         for statement in data.statements.iter().rev() {
             statement_index -= 1;
-            let statement_location = Location { block, statement_index };
+            let statement_location = Location {
+                block,
+                statement_index,
+            };
             let statement_defs_uses = self.defs_uses(mir, statement_location, statement);
             statement_defs_uses.apply(&mut bits);
             callback(statement_location, &bits);
@@ -133,27 +165,32 @@ pub fn simulate_block<'tcx, OP>(&self,
         assert_eq!(bits, self.ins[block]);
     }
 
-    fn defs_uses<'tcx, V>(&self,
-                          mir: &Mir<'tcx>,
-                          location: Location,
-                          thing: &V)
-                          -> DefsUses
-        where V: MirVisitable<'tcx>,
+    fn defs_uses<'tcx, V>(&self, mir: &Mir<'tcx>, location: Location, thing: &V) -> DefsUses
+    where
+        V: MirVisitable<'tcx>,
     {
         let locals = mir.local_decls.len();
-        let mut visitor = DefsUses {
-            defs: LocalSet::new_empty(locals),
-            uses: LocalSet::new_empty(locals),
+        let mut visitor = DefsUsesVisitor {
+            mode: self.mode,
+            defs_uses: DefsUses {
+                defs: LocalSet::new_empty(locals),
+                uses: LocalSet::new_empty(locals),
+            },
         };
 
         // Visit the various parts of the basic block in reverse. If we go
         // forward, the logic in `add_def` and `add_use` would be wrong.
         thing.apply(location, &mut visitor);
 
-        visitor
+        visitor.defs_uses
     }
 }
 
+struct DefsUsesVisitor {
+    mode: LivenessMode,
+    defs_uses: DefsUses,
+}
+
 #[derive(Eq, PartialEq, Clone)]
 struct DefsUses {
     defs: LocalSet,
@@ -195,18 +232,15 @@ fn add_use(&mut self, index: Local) {
     }
 }
 
-impl<'tcx> Visitor<'tcx> for DefsUses {
-    fn visit_local(&mut self,
-                   &local: &Local,
-                   context: LvalueContext<'tcx>,
-                   _: Location) {
+impl<'tcx> Visitor<'tcx> for DefsUsesVisitor {
+    fn visit_local(&mut self, &local: &Local, context: LvalueContext<'tcx>, _: Location) {
         match context {
             ///////////////////////////////////////////////////////////////////////////
             // DEFS
 
             LvalueContext::Store |
 
-            // We let Call defined the result in both the success and
+            // We let Call define the result in both the success and
             // unwind cases. This is not really correct, however it
             // does not seem to be observable due to the way that we
             // generate MIR. See the test case
@@ -220,11 +254,15 @@ fn visit_local(&mut self,
             // values that come before them.
             LvalueContext::StorageLive |
             LvalueContext::StorageDead => {
-                self.add_def(local);
+                self.defs_uses.add_def(local);
             }
 
             ///////////////////////////////////////////////////////////////////////////
-            // USES
+            // REGULAR USES
+            //
+            // These are uses that occur *outside* of a drop. For the
+            // purposes of NLL, these are special in that **all** the
+            // lifetimes appearing in the variable must be live for each regular use.
 
             LvalueContext::Projection(..) |
 
@@ -236,25 +274,42 @@ fn visit_local(&mut self,
 
             LvalueContext::Inspect |
             LvalueContext::Consume |
-            LvalueContext::Validate |
+            LvalueContext::Validate => {
+                if self.mode.include_regular_use {
+                    self.defs_uses.add_use(local);
+                }
+            }
+
+            ///////////////////////////////////////////////////////////////////////////
+            // DROP USES
+            //
+            // These are uses that occur in a DROP (a MIR drop, not a
+            // call to `std::mem::drop()`). For the purposes of NLL,
+            // uses in drop are special because `#[may_dangle]`
+            // attributes can affect whether lifetimes must be live.
 
-            // We consider drops to always be uses of locals.
-            // Drop eloboration should be run before this analysis otherwise
-            // the results might be too pessimistic.
             LvalueContext::Drop => {
-                self.add_use(local);
+                if self.mode.include_drops {
+                    self.defs_uses.add_use(local);
+                }
             }
         }
     }
 }
 
-fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses {
-    let mut visitor = DefsUses {
-        defs: LocalSet::new_empty(locals),
-        uses: LocalSet::new_empty(locals),
+fn block<'tcx>(mode: LivenessMode, b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses {
+    let mut visitor = DefsUsesVisitor {
+        mode,
+        defs_uses: DefsUses {
+            defs: LocalSet::new_empty(locals),
+            uses: LocalSet::new_empty(locals),
+        },
     };
 
-    let dummy_location = Location { block: BasicBlock::new(0), statement_index: 0 };
+    let dummy_location = Location {
+        block: BasicBlock::new(0),
+        statement_index: 0,
+    };
 
     // Visit the various parts of the basic block in reverse. If we go
     // forward, the logic in `add_def` and `add_use` would be wrong.
@@ -263,62 +318,64 @@ fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses {
         visitor.visit_statement(BasicBlock::new(0), statement, dummy_location);
     }
 
-    visitor
+    visitor.defs_uses
 }
 
 trait MirVisitable<'tcx> {
     fn apply<V>(&self, location: Location, visitor: &mut V)
-        where V: Visitor<'tcx>;
+    where
+        V: Visitor<'tcx>;
 }
 
 impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> {
     fn apply<V>(&self, location: Location, visitor: &mut V)
-        where V: Visitor<'tcx>
+    where
+        V: Visitor<'tcx>,
     {
-        visitor.visit_statement(location.block,
-                                self,
-                                location)
+        visitor.visit_statement(location.block, self, location)
     }
 }
 
 impl<'tcx> MirVisitable<'tcx> for Option<Terminator<'tcx>> {
     fn apply<V>(&self, location: Location, visitor: &mut V)
-        where V: Visitor<'tcx>
+    where
+        V: Visitor<'tcx>,
     {
-        visitor.visit_terminator(location.block,
-                                 self.as_ref().unwrap(),
-                                 location)
+        visitor.visit_terminator(location.block, self.as_ref().unwrap(), location)
     }
 }
 
-pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                          pass_name: &str,
-                          source: MirSource,
-                          mir: &Mir<'tcx>,
-                          result: &LivenessResult) {
+pub fn dump_mir<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    pass_name: &str,
+    source: MirSource,
+    mir: &Mir<'tcx>,
+    result: &LivenessResult,
+) {
     if !dump_enabled(tcx, pass_name, source) {
         return;
     }
-    let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below
+    let node_path = item_path::with_forced_impl_filename_line(|| {
+        // see notes on #41697 below
         tcx.item_path_str(tcx.hir.local_def_id(source.item_id()))
     });
-    dump_matched_mir_node(tcx, pass_name, &node_path,
-                          source, mir, result);
+    dump_matched_mir_node(tcx, pass_name, &node_path, source, mir, result);
 }
 
-fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                   pass_name: &str,
-                                   node_path: &str,
-                                   source: MirSource,
-                                   mir: &Mir<'tcx>,
-                                   result: &LivenessResult) {
+fn dump_matched_mir_node<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    pass_name: &str,
+    node_path: &str,
+    source: MirSource,
+    mir: &Mir<'tcx>,
+    result: &LivenessResult,
+) {
     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{}{}-liveness.mir",
-                            source.item_id(), pass_name);
+    let file_name = format!("rustc.node{}{}-liveness.mir", source.item_id(), pass_name);
     file_path.push(&file_name);
     let _ = fs::File::create(&file_path).and_then(|mut file| {
         writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?;
@@ -330,16 +387,18 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     });
 }
 
-pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                              src: MirSource,
-                              mir: &Mir<'tcx>,
-                              w: &mut Write,
-                              result: &LivenessResult)
-                              -> io::Result<()> {
+pub fn write_mir_fn<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    src: MirSource,
+    mir: &Mir<'tcx>,
+    w: &mut Write,
+    result: &LivenessResult,
+) -> io::Result<()> {
     write_mir_intro(tcx, src, mir, w)?;
     for block in mir.basic_blocks().indices() {
         let print = |w: &mut Write, prefix, result: &IndexVec<BasicBlock, LocalSet>| {
-            let live: Vec<String> = mir.local_decls.indices()
+            let live: Vec<String> = mir.local_decls
+                .indices()
                 .filter(|i| result[block].contains(i))
                 .map(|i| format!("{:?}", i))
                 .collect();
@@ -356,4 +415,3 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     writeln!(w, "}}")?;
     Ok(())
 }
-
index f0d56db0573b6a27887754c9c1032b2a3cf70753..e9af37a1f64c8338276fc9b0c05f44b0319e8aba 100644 (file)
@@ -26,21 +26,26 @@ fn main() {
 //
 // END RUST SOURCE
 // START rustc.node12.nll.0.mir
-//    | Variables live on entry to the block bb0:
+//    | Variables regular-live on entry to the block bb0: []
+//    | Variables drop-live on entry to the block bb0: []
 //    bb0: {
-//        | Live variables here: []
+//        | Regular-Live variables here: []
+//        | Drop-Live variables here: []
 //        StorageLive(_1);
-//        | Live variables here: []
+//        | Regular-Live variables here: []
+//        | Drop-Live variables here: []
 //        _1 = const <std::boxed::Box<T>>::new(const 22usize) -> bb1;
 //    }
 // END rustc.node12.nll.0.mir
 // START rustc.node12.nll.0.mir
-//    | Variables live on entry to the block bb1:
-//    | - _1
+//    | Variables regular-live on entry to the block bb1: []
+//    | Variables drop-live on entry to the block bb1: [_1]
 //    bb1: {
-//        | Live variables here: [_1]
+//        | Regular-Live variables here: []
+//        | Drop-Live variables here: [_1]
 //        StorageLive(_2);
-//        | Live variables here: [_1]
+//        | Regular-Live variables here: []
+//        | Drop-Live variables here: [_1]
 //        _2 = const can_panic() -> [return: bb2, unwind: bb4];
 //    }
 // END rustc.node12.nll.0.mir
index 1fac9484bdb3205875d8194b55f6a5e970f4916e..957a57428e3b3cfff4238ed71331990b8bf504c4 100644 (file)
@@ -25,17 +25,23 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.node12.nll.0.mir
-//    | Variables live on entry to the block bb1:
+//    | Variables regular-live on entry to the block bb1: []
+//    | Variables drop-live on entry to the block bb1: []
 //    bb1: {
-//        | Live variables here: []
+//        | Regular-Live variables here: []
+//        | Drop-Live variables here: []
 //        _1 = const 55usize;
-//        | Live variables here: [_1]
+//        | Regular-Live variables here: [_1]
+//        | Drop-Live variables here: []
 //        StorageLive(_3);
-//        | Live variables here: [_1]
+//        | Regular-Live variables here: [_1]
+//        | Drop-Live variables here: []
 //        StorageLive(_4);
-//        | Live variables here: [_1]
+//        | Regular-Live variables here: [_1]
+//        | Drop-Live variables here: []
 //        _4 = _1;
-//        | Live variables here: [_4]
+//        | Regular-Live variables here: [_4]
+//        | Drop-Live variables here: []
 //        _3 = const use_x(_4) -> bb2;
 //    }
 // END rustc.node12.nll.0.mir
index 4380698e704f396b8cdf7b37f46ac6438de43c06..f5a2a25a9b0c1e69a97309a6d17b3d98570326b6 100644 (file)
@@ -29,21 +29,26 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.node18.nll.0.mir
-//     | Variables live on entry to the block bb2:
-//     | - _1
+//    | Variables regular-live on entry to the block bb2: [_1]
+//    | Variables drop-live on entry to the block bb2: []
 //     bb2: {
-//         | Live variables here: [_1]
+//         | Regular-Live variables here: [_1]
+//         | Drop-Live variables here: []
 //         StorageLive(_4);
-//         | Live variables here: [_1]
+//         | Regular-Live variables here: [_1]
+//         | Drop-Live variables here: []
 //         _4 = _1;
-//         | Live variables here: [_4]
+//         | Regular-Live variables here: [_4]
+//         | Drop-Live variables here: []
 //         _3 = const make_live(_4) -> bb4;
 //     }
 // END rustc.node18.nll.0.mir
 // START rustc.node18.nll.0.mir
-//     | Variables live on entry to the block bb3:
+//     | Variables regular-live on entry to the block bb3: []
+//     | Variables drop-live on entry to the block bb3: []
 //     bb3: {
-//         | Live variables here: []
+//         | Regular-Live variables here: []
+//         | Drop-Live variables here: []
 //         _5 = const make_dead() -> bb5;
 //     }
 // END rustc.node18.nll.0.mir
index d1ef08cf00d7d67da9563d566df4db2951ddcfa6..67e16c2fe6fc3bda13a6e6681efc1796f1c0d238 100644 (file)
@@ -37,19 +37,24 @@ fn main() {
 // END rustc.node12.nll.0.mir
 // START rustc.node12.nll.0.mir
 //    bb1: {
-//        | Live variables here: [_1, _3]
+//        | Regular-Live variables here: [_1, _3]
+//        | Drop-Live variables here: []
 //        _2 = &'_#0r _1[_3];
-//        | Live variables here: [_2]
+//        | Regular-Live variables here: [_2]
+//        | Drop-Live variables here: []
 //        switchInt(const true) -> [0u8: bb3, otherwise: bb2];
 //    }
 // END rustc.node12.nll.0.mir
 // START rustc.node12.nll.0.mir
 //    bb2: {
-//        | Live variables here: [_2]
+//        | Regular-Live variables here: [_2]
+//        | Drop-Live variables here: []
 //        StorageLive(_7);
-//        | Live variables here: [_2]
+//        | Regular-Live variables here: [_2]
+//        | Drop-Live variables here: []
 //        _7 = (*_2);
-//        | Live variables here: [_7]
+//        | Regular-Live variables here: [_7]
+//        | Drop-Live variables here: []
 //        _6 = const use_x(_7) -> bb4;
 //    }
 // END rustc.node12.nll.0.mir
diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs
new file mode 100644 (file)
index 0000000..7482288
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright 2012-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.
+
+// Basic test for liveness constraints: the region (`R1`) that appears
+// in the type of `p` includes the points after `&v[0]` up to (but not
+// including) the call to `use_x`. The `else` branch is not included.
+
+// compile-flags:-Znll -Zverbose
+//                     ^^^^^^^^^ force compiler to dump more region information
+
+#![allow(warnings)]
+#![feature(dropck_eyepatch)]
+#![feature(generic_param_attrs)]
+
+fn use_x(_: usize) -> bool { true }
+
+fn main() {
+    let mut v = [1, 2, 3];
+    let p: Wrap<& /* R4 */ usize> = Wrap { value: &v[0] };
+    if true {
+        use_x(*p.value);
+    } else {
+        use_x(22);
+    }
+
+    // `p` will get dropped here. However, because of the
+    // `#[may_dangle]` attribute, we do not need to consider R4 live.
+}
+
+struct Wrap<T> {
+    value: T
+}
+
+unsafe impl<#[may_dangle] T> Drop for Wrap<T> {
+    fn drop(&mut self) { }
+}
+
+// END RUST SOURCE
+// START rustc.node12.nll.0.mir
+// | R4: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]}
+// END rustc.node12.nll.0.mir