From c42a64518af371fe7cc6b70607b5c35144b96575 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 10:42:47 -0400 Subject: [PATCH 01/33] execute liveness, write a simple test --- src/librustc_mir/transform/nll/mod.rs | 28 ++++++++++-- src/test/mir-opt/nll/liveness-interblock.rs | 47 +++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/test/mir-opt/nll/liveness-interblock.rs diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 805e9c976e4..ebc241e44ff 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -21,6 +21,7 @@ use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use syntax_pos::DUMMY_SP; use std::collections::HashMap; use std::fmt; +use util::liveness; use util as mir_util; use self::mir_util::PassWhere; @@ -151,13 +152,34 @@ impl MirPass for NLL { tcx.infer_ctxt().enter(|infcx| { // Clone mir so we can mutate it without disturbing the rest of the compiler let mut renumbered_mir = mir.clone(); + let mut visitor = NLLVisitor::new(&infcx); visitor.visit_mir(&mut renumbered_mir); + + let liveness = liveness::liveness_of_locals(&renumbered_mir); + mir_util::dump_mir(tcx, None, "nll", &0, source, mir, |pass_where, out| { - if let PassWhere::BeforeCFG = pass_where { - for (index, value) in visitor.regions.iter_enumerated() { - writeln!(out, "// R{:03}: {:?}", index.0, value)?; + match pass_where { + // Before the CFG, dump out the values for each region variable. + PassWhere::BeforeCFG => { + for (index, value) in visitor.regions.iter_enumerated() { + writeln!(out, "| R{:03}: {:?}", index.0, value)?; + } } + + // 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)?; + } + } + + PassWhere::InCFG(_) => { } + + PassWhere::AfterCFG => { } } Ok(()) }); diff --git a/src/test/mir-opt/nll/liveness-interblock.rs b/src/test/mir-opt/nll/liveness-interblock.rs new file mode 100644 index 00000000000..e3c7ab3cc0c --- /dev/null +++ b/src/test/mir-opt/nll/liveness-interblock.rs @@ -0,0 +1,47 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll + +fn cond() -> bool { false } + +fn make_live(x: usize) { } + +fn make_dead() { } + +fn main() { + let x = 5; + + if cond() { + make_live(x); + } else { + // x should be dead on entry to this block + make_dead(); + } +} + +// END RUST SOURCE +// START rustc.node18.nll.0.mir +// | Variables live on entry to the block bb2: +// | - _1 +// bb2: { +// StorageLive(_4); +// _4 = _1; +// _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: +// bb3: { +// _5 = const make_dead() -> bb5; +// } +// END rustc.node18.nll.0.mir + + From acc5c4345c42c7558c15ffdf1e10a20126652c73 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 11:06:58 -0400 Subject: [PATCH 02/33] add a test for the subtle case around calls --- .../mir-opt/nll/liveness-call-subtlety.rs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/test/mir-opt/nll/liveness-call-subtlety.rs diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs new file mode 100644 index 00000000000..99ed5fe186e --- /dev/null +++ b/src/test/mir-opt/nll/liveness-call-subtlety.rs @@ -0,0 +1,42 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll + +fn can_panic() -> Box { + Box::new(44) +} + +fn main() { + let mut x = Box::new(22); + x = can_panic(); +} + +// Check that: +// - `_1` is the variable corresponding to `x` +// and +// - `_1` is live when `can_panic` is called (because it may be dropped) +// +// END RUST SOURCE +// START rustc.node12.nll.0.mir +// | Variables live on entry to the block bb0: +// bb0: { +// StorageLive(_1); // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:18:9: 18:14 +// _1 = const >::new(const 22usize) -> bb1; // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:18:17: 18:29 +// } +// END rustc.node12.nll.0.mir +// START rustc.node12.nll.0.mir +// | Variables live on entry to the block bb1: +// | - _1 +// bb1: { +// StorageLive(_2); // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:19:9: 19:20 +// _2 = const can_panic() -> [return: bb2, unwind: bb4]; // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:19:9: 19:20 +// } +// END rustc.node12.nll.0.mir From 9a47fd2dacbb7e311ecfe8e56aa97c99b1e3dc5c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 11:28:25 -0400 Subject: [PATCH 03/33] factor out `pre_defs` field by going backwards --- src/librustc_mir/util/liveness.rs | 66 +++++++++++++++---- .../mir-opt/nll/liveness-drop-intra-block.rs | 36 ++++++++++ 2 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 src/test/mir-opt/nll/liveness-drop-intra-block.rs diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index 1424c063d73..b9d90051b85 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -54,29 +54,72 @@ struct BlockInfo { } struct BlockInfoVisitor { - pre_defs: LocalSet, defs: LocalSet, uses: LocalSet, } +impl BlockInfoVisitor { + fn add_def(&mut self, index: Local) { + // If it was used already in the block, remove that use + // now that we found a definition. + // + // Example: + // + // // Defs = {X}, Uses = {} + // X = 5 + // // Defs = {}, Uses = {X} + // use(X) + self.uses.remove(&index); + self.defs.add(&index); + } + + fn add_use(&mut self, index: Local) { + // Inverse of above. + // + // Example: + // + // // Defs = {}, Uses = {X} + // use(X) + // // Defs = {X}, Uses = {} + // X = 5 + // // Defs = {}, Uses = {X} + // use(X) + self.defs.remove(&index); + self.uses.add(&index); + } +} + impl<'tcx> Visitor<'tcx> for BlockInfoVisitor { 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 unwind cases. - // This may not be right. + // We let Call defined 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 + // `mir-opt/nll/liveness-call-subtlety.rs`. To do things + // properly, we would apply the def in call only to the + // input from the success path and not the unwind + // path. -nmatsakis LvalueContext::Call | // Storage live and storage dead aren't proper defines, but we can ignore // values that come before them. LvalueContext::StorageLive | LvalueContext::StorageDead => { - self.defs.add(&local); + self.add_def(local); } + + /////////////////////////////////////////////////////////////////////////// + // USES + LvalueContext::Projection(..) | // Borrows only consider their local used at the point of the borrow. @@ -93,10 +136,7 @@ impl<'tcx> Visitor<'tcx> for BlockInfoVisitor { // Drop eloboration should be run before this analysis otherwise // the results might be too pessimistic. LvalueContext::Drop => { - // Ignore uses which are already defined in this block - if !self.pre_defs.contains(&local) { - self.uses.add(&local); - } + self.add_use(local); } } } @@ -104,18 +144,18 @@ impl<'tcx> Visitor<'tcx> for BlockInfoVisitor { fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> BlockInfo { let mut visitor = BlockInfoVisitor { - pre_defs: LocalSet::new_empty(locals), defs: LocalSet::new_empty(locals), uses: LocalSet::new_empty(locals), }; let dummy_location = Location { block: BasicBlock::new(0), statement_index: 0 }; - for statement in &b.statements { - visitor.visit_statement(BasicBlock::new(0), statement, dummy_location); - visitor.pre_defs.union(&visitor.defs); - } + // 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. visitor.visit_terminator(BasicBlock::new(0), b.terminator(), dummy_location); + for statement in b.statements.iter().rev() { + visitor.visit_statement(BasicBlock::new(0), statement, dummy_location); + } BlockInfo { defs: visitor.defs, diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs new file mode 100644 index 00000000000..2d7aceb558f --- /dev/null +++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs @@ -0,0 +1,36 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll + +#![allow(warnings)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut x = 22; + loop { + // Key point: `x` not live on entry to this basic block. + x = 55; + if use_x(x) { break; } + } +} + +// END RUST SOURCE +// START rustc.node12.nll.0.mir +// | Variables live on entry to the block bb1: +// bb1: { +// _1 = const 55usize; +// StorageLive(_3); +// StorageLive(_4); +// _4 = _1; +// _3 = const use_x(_4) -> bb2; +// } +// END rustc.node12.nll.0.mir From 16b5f244fb21ebbec2bf3a27820de44a56fbd8b0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 11:31:03 -0400 Subject: [PATCH 04/33] rename `BlockInfo` and `BlockInfoVisitor` to `DefsUses` --- src/librustc_mir/util/liveness.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index b9d90051b85..d0a37e8a224 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -48,17 +48,12 @@ use std::io::{self, Write}; pub type LocalSet = IdxSetBuf; #[derive(Eq, PartialEq, Clone)] -struct BlockInfo { +struct DefsUses { defs: LocalSet, uses: LocalSet, } -struct BlockInfoVisitor { - defs: LocalSet, - uses: LocalSet, -} - -impl BlockInfoVisitor { +impl DefsUses { fn add_def(&mut self, index: Local) { // If it was used already in the block, remove that use // now that we found a definition. @@ -89,7 +84,7 @@ impl BlockInfoVisitor { } } -impl<'tcx> Visitor<'tcx> for BlockInfoVisitor { +impl<'tcx> Visitor<'tcx> for DefsUses { fn visit_local(&mut self, &local: &Local, context: LvalueContext<'tcx>, @@ -142,8 +137,8 @@ impl<'tcx> Visitor<'tcx> for BlockInfoVisitor { } } -fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> BlockInfo { - let mut visitor = BlockInfoVisitor { +fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses { + let mut visitor = DefsUses { defs: LocalSet::new_empty(locals), uses: LocalSet::new_empty(locals), }; @@ -157,10 +152,7 @@ fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> BlockInfo { visitor.visit_statement(BasicBlock::new(0), statement, dummy_location); } - BlockInfo { - defs: visitor.defs, - uses: visitor.uses, - } + visitor } // This gives the result of the liveness analysis at the boundary of basic blocks From bd2511652fec706c306a3c11742e69756a4d0fb1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 11:55:03 -0400 Subject: [PATCH 05/33] extend `dump_mir` to work for any tcx, not just global tcx --- src/librustc/mir/transform.rs | 4 +- src/librustc_mir/util/pretty.rs | 68 ++++++++++++++++----------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index f29405e6650..92e62b14693 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -39,13 +39,13 @@ pub enum MirSource { GeneratorDrop(NodeId), } -impl<'a, 'tcx> MirSource { +impl<'a, 'gcx, 'tcx> MirSource { pub fn from_local_def_id(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> MirSource { let id = tcx.hir.as_local_node_id(def_id).expect("mir source requires local def-id"); Self::from_node(tcx, id) } - pub fn from_node(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: NodeId) -> MirSource { + pub fn from_node(tcx: TyCtxt<'a, 'gcx, 'tcx>, id: NodeId) -> MirSource { use hir::*; // Handle constants in enum discriminants, types, and repeat expressions. diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 9f61feabe41..1d924175b21 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -56,13 +56,13 @@ pub enum PassWhere { /// - `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, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_num: Option<(MirSuite, MirPassIndex)>, - pass_name: &str, - disambiguator: &Display, - source: MirSource, - mir: &Mir<'tcx>, - extra_data: F) +pub fn dump_mir<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + pass_num: Option<(MirSuite, MirPassIndex)>, + pass_name: &str, + disambiguator: &Display, + source: MirSource, + mir: &Mir<'tcx>, + extra_data: F) where F: FnMut(PassWhere, &mut Write) -> io::Result<()> { @@ -77,10 +77,10 @@ where disambiguator, source, mir, extra_data); } -pub fn dump_enabled<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_name: &str, - source: MirSource) - -> bool { +pub fn dump_enabled<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + pass_name: &str, + source: MirSource) + -> bool { let filters = match tcx.sess.opts.debugging_opts.dump_mir { None => return false, Some(ref filters) => filters, @@ -101,14 +101,14 @@ pub fn dump_enabled<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // `item_path_str()` would otherwise trigger `type_of`, and this can // run while we are already attempting to evaluate `type_of`. -fn dump_matched_mir_node<'a, 'tcx, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_num: Option<(MirSuite, MirPassIndex)>, - pass_name: &str, - node_path: &str, - disambiguator: &Display, - source: MirSource, - mir: &Mir<'tcx>, - mut extra_data: F) +fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + pass_num: Option<(MirSuite, MirPassIndex)>, + pass_name: &str, + node_path: &str, + disambiguator: &Display, + source: MirSource, + mir: &Mir<'tcx>, + mut extra_data: F) where F: FnMut(PassWhere, &mut Write) -> io::Result<()> { @@ -161,10 +161,10 @@ where } /// Write out a human-readable textual representation for the given MIR. -pub fn write_mir_pretty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - single: Option, - w: &mut Write) - -> io::Result<()> +pub fn write_mir_pretty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + single: Option, + w: &mut Write) + -> io::Result<()> { writeln!(w, "// WARNING: This output format is intended for human consumers only")?; writeln!(w, "// and is subject to change without notice. Knock yourself out.")?; @@ -192,12 +192,12 @@ pub fn write_mir_pretty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, Ok(()) } -pub fn write_mir_fn<'a, 'tcx, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, - mir: &Mir<'tcx>, - extra_data: &mut F, - w: &mut Write) - -> io::Result<()> +pub fn write_mir_fn<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + src: MirSource, + mir: &Mir<'tcx>, + extra_data: &mut F, + w: &mut Write) + -> io::Result<()> where F: FnMut(PassWhere, &mut Write) -> io::Result<()> { @@ -321,11 +321,11 @@ fn write_scope_tree(tcx: TyCtxt, /// 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). -pub fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, - mir: &Mir, - w: &mut Write) - -> io::Result<()> { +pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + src: MirSource, + mir: &Mir, + w: &mut Write) + -> io::Result<()> { write_mir_sig(tcx, src, mir, w)?; writeln!(w, " {{")?; From ea03a43fe67f06a13654dc7ddf89e62ba9dbe2e9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 11:57:22 -0400 Subject: [PATCH 06/33] introduce `apply` helper that applies a DefUse set to live bits --- src/librustc_mir/util/liveness.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index d0a37e8a224..d3a10225cdc 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -54,6 +54,10 @@ struct DefsUses { } impl DefsUses { + fn apply(&self, bits: &mut LocalSet) -> bool { + bits.subtract(&self.defs) | bits.union(&self.uses) + } + fn add_def(&mut self, index: Local) { // If it was used already in the block, remove that use // now that we found a definition. @@ -194,8 +198,9 @@ pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult { // in = use ∪ (out - def) ins[b].clone_from(&outs[b]); - ins[b].subtract(&def_use[b].defs); - ins[b].union(&def_use[b].uses); + + // FIXME use the return value to detect if we have changed things + def_use[b].apply(&mut ins[b]); } if ins_ == ins && outs_ == outs { From 1f06ba486f9a7ac92aca81ab4960e251646f3b97 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 13:32:00 -0400 Subject: [PATCH 07/33] extend liveness to compute intrablock liveness and add unit tests --- src/librustc_mir/transform/nll/mod.rs | 40 +++++++-- src/librustc_mir/util/liveness.rs | 84 +++++++++++++++++++ .../mir-opt/nll/liveness-call-subtlety.rs | 12 ++- .../mir-opt/nll/liveness-drop-intra-block.rs | 5 ++ src/test/mir-opt/nll/liveness-interblock.rs | 6 +- 5 files changed, 134 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index ebc241e44ff..fc42fd42b15 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -16,7 +16,7 @@ use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, Lookup}; use rustc::mir::transform::{MirPass, MirSource}; use rustc::infer::{self as rustc_infer, InferCtxt}; -use rustc::util::nodemap::FxHashSet; +use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use syntax_pos::DUMMY_SP; use std::collections::HashMap; @@ -144,21 +144,34 @@ impl MirPass for NLL { fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, - mir: &mut Mir<'tcx>) { + input_mir: &mut Mir<'tcx>) { if !tcx.sess.opts.debugging_opts.nll { return; } tcx.infer_ctxt().enter(|infcx| { // Clone mir so we can mutate it without disturbing the rest of the compiler - let mut renumbered_mir = mir.clone(); + let mir = &mut input_mir.clone(); let mut visitor = NLLVisitor::new(&infcx); - visitor.visit_mir(&mut renumbered_mir); + visitor.visit_mir(mir); - let liveness = liveness::liveness_of_locals(&renumbered_mir); + let liveness = liveness::liveness_of_locals(mir); - mir_util::dump_mir(tcx, None, "nll", &0, source, mir, |pass_where, out| { + let liveness_per_location: FxHashMap<_, _> = + mir + .basic_blocks() + .indices() + .flat_map(|bb| { + let mut results = vec![]; + liveness.simulate_block(&mir, bb, |location, local_set| { + results.push((location, local_set.clone())); + }); + results + }) + .collect(); + + mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| { match pass_where { // Before the CFG, dump out the values for each region variable. PassWhere::BeforeCFG => { @@ -177,7 +190,18 @@ impl MirPass for NLL { } } - PassWhere::InCFG(_) => { } + PassWhere::InCFG(location) => { + let local_set = &liveness_per_location[&location]; + let mut string = String::new(); + for local in local_set.iter() { + string.push_str(&format!(", {:?}", local)); + } + if !string.is_empty() { + writeln!(out, " | Live variables here: [{}]", &string[2..])?; + } else { + writeln!(out, " | Live variables here: []")?; + } + } PassWhere::AfterCFG => { } } @@ -185,7 +209,7 @@ impl MirPass for NLL { }); let (_lookup_map, regions) = visitor.into_results(); let mut inference_context = InferenceContext::new(regions); - inference_context.solve(&infcx, &renumbered_mir); + inference_context.solve(&infcx, mir); }) } } diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index d3a10225cdc..7e4529e9b5e 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -214,6 +214,90 @@ pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult { } } +impl LivenessResult { + /// Walks backwards through the statements/terminator in the given + /// 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) + { + let data = &mir[block]; + + // Get a copy of the bits on exit from the block. + let mut bits = self.outs[block].clone(); + + // Start with the maximal statement index -- i.e., right before + // the terminator executes. + let mut statement_index = data.statements.len(); + + // Compute liveness right before terminator and invoke callback. + 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); + + // 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_defs_uses = self.defs_uses(mir, statement_location, statement); + statement_defs_uses.apply(&mut bits); + callback(statement_location, &bits); + } + + assert_eq!(bits, self.ins[block]); + } + + 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), + }; + + // 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 + } +} + +trait MirVisitable<'tcx> { + fn apply(&self, location: Location, visitor: &mut V) + where V: Visitor<'tcx>; +} + +impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> { + fn apply(&self, location: Location, visitor: &mut V) + where V: Visitor<'tcx> + { + visitor.visit_statement(location.block, + self, + location) + } +} + +impl<'tcx> MirVisitable<'tcx> for Option> { + fn apply(&self, location: Location, visitor: &mut V) + where V: Visitor<'tcx> + { + 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, diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs index 99ed5fe186e..f0d56db0573 100644 --- a/src/test/mir-opt/nll/liveness-call-subtlety.rs +++ b/src/test/mir-opt/nll/liveness-call-subtlety.rs @@ -28,15 +28,19 @@ fn main() { // START rustc.node12.nll.0.mir // | Variables live on entry to the block bb0: // bb0: { -// StorageLive(_1); // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:18:9: 18:14 -// _1 = const >::new(const 22usize) -> bb1; // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:18:17: 18:29 +// | Live variables here: [] +// StorageLive(_1); +// | Live variables here: [] +// _1 = const >::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 // bb1: { -// StorageLive(_2); // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:19:9: 19:20 -// _2 = const can_panic() -> [return: bb2, unwind: bb4]; // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:19:9: 19:20 +// | Live variables here: [_1] +// StorageLive(_2); +// | Live variables here: [_1] +// _2 = const can_panic() -> [return: bb2, unwind: bb4]; // } // END rustc.node12.nll.0.mir diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs index 2d7aceb558f..1fac9484bdb 100644 --- a/src/test/mir-opt/nll/liveness-drop-intra-block.rs +++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs @@ -27,10 +27,15 @@ fn main() { // START rustc.node12.nll.0.mir // | Variables live on entry to the block bb1: // bb1: { +// | Live variables here: [] // _1 = const 55usize; +// | Live variables here: [_1] // StorageLive(_3); +// | Live variables here: [_1] // StorageLive(_4); +// | Live variables here: [_1] // _4 = _1; +// | Live variables here: [_4] // _3 = const use_x(_4) -> bb2; // } // END rustc.node12.nll.0.mir diff --git a/src/test/mir-opt/nll/liveness-interblock.rs b/src/test/mir-opt/nll/liveness-interblock.rs index e3c7ab3cc0c..4380698e704 100644 --- a/src/test/mir-opt/nll/liveness-interblock.rs +++ b/src/test/mir-opt/nll/liveness-interblock.rs @@ -12,7 +12,7 @@ fn cond() -> bool { false } -fn make_live(x: usize) { } +fn make_live(_: usize) { } fn make_dead() { } @@ -32,14 +32,18 @@ fn main() { // | Variables live on entry to the block bb2: // | - _1 // bb2: { +// | Live variables here: [_1] // StorageLive(_4); +// | Live variables here: [_1] // _4 = _1; +// | Live variables here: [_4] // _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: // bb3: { +// | Live variables here: [] // _5 = const make_dead() -> bb5; // } // END rustc.node18.nll.0.mir From 8535a4a32c2b11b0ecadd00d857604fed81e869e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 14:20:32 -0400 Subject: [PATCH 08/33] add helper `for_each_free_region` that iterates over free regions --- src/librustc/ty/fold.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index edd4329fa41..149999e0eee 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -218,6 +218,43 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { { value.fold_with(&mut RegionFolder::new(self, skipped_regions, &mut f)) } + + pub fn for_each_free_region(self, + value: &T, + callback: F) + where F: FnMut(ty::Region<'tcx>), + T: TypeFoldable<'tcx>, + { + value.visit_with(&mut RegionVisitor { current_depth: 0, callback }); + + struct RegionVisitor { + current_depth: u32, + callback: F, + } + + impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor + where F : FnMut(ty::Region<'tcx>) + { + fn visit_binder>(&mut self, t: &Binder) -> bool { + self.current_depth += 1; + t.skip_binder().visit_with(self); + self.current_depth -= 1; + + false // keep visiting + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + match *r { + ty::ReLateBound(debruijn, _) if debruijn.depth < self.current_depth => { + /* ignore bound regions */ + } + _ => (self.callback)(r), + } + + false // keep visiting + } + } + } } /// Folds over the substructure of a type, visiting its component From 7523c7368c9e875eae6da46091cbf86c48041b89 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 14:20:47 -0400 Subject: [PATCH 09/33] introduce liveness constraints into NLL code And do a bunch of gratuitious refactoring that I did not bother to separate into nice commits. --- src/librustc_data_structures/indexed_set.rs | 12 +- src/librustc_mir/lib.rs | 1 + .../transform/nll/constraint_generation.rs | 64 ++++ src/librustc_mir/transform/nll/mod.rs | 283 +++++++----------- .../nll/{infer.rs => region_infer.rs} | 55 ++-- src/librustc_mir/transform/nll/renumber.rs | 132 ++++++++ src/test/mir-opt/nll/region-liveness-basic.rs | 55 ++++ 7 files changed, 393 insertions(+), 209 deletions(-) create mode 100644 src/librustc_mir/transform/nll/constraint_generation.rs rename src/librustc_mir/transform/nll/{infer.rs => region_infer.rs} (85%) create mode 100644 src/librustc_mir/transform/nll/renumber.rs create mode 100644 src/test/mir-opt/nll/region-liveness-basic.rs diff --git a/src/librustc_data_structures/indexed_set.rs b/src/librustc_data_structures/indexed_set.rs index c790463e47a..c5ffb003399 100644 --- a/src/librustc_data_structures/indexed_set.rs +++ b/src/librustc_data_structures/indexed_set.rs @@ -53,11 +53,19 @@ pub struct IdxSet { } impl fmt::Debug for IdxSetBuf { - fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) } + fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { + w.debug_list() + .entries(self.iter()) + .finish() + } } impl fmt::Debug for IdxSet { - fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) } + fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { + w.debug_list() + .entries(self.iter()) + .finish() + } } impl IdxSetBuf { diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 7e4206e14c5..16753cee7e0 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -18,6 +18,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(conservative_impl_trait)] #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(i128_type)] diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs new file mode 100644 index 00000000000..1c0c3b6fc33 --- /dev/null +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -0,0 +1,64 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::mir::Mir; +use rustc::infer::InferCtxt; +use util::liveness::LivenessResult; + +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(); +} + +struct ConstraintGeneration<'constrain, 'gcx: 'tcx, 'tcx: 'constrain> { + infcx: &'constrain InferCtxt<'constrain, 'gcx, 'tcx>, + regioncx: &'constrain mut RegionInferenceContext, + mir: &'constrain Mir<'tcx>, + liveness: &'constrain LivenessResult, +} + +impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> { + fn add_constraints(&mut self) { + // To start, add the liveness constraints. + self.add_liveness_constraints(); + } + + /// Liveness constraints: + /// + /// > If a variable V is live at point P, then all regions R in the type of V + /// > must include the point P. + fn add_liveness_constraints(&mut self) { + let tcx = self.infcx.tcx; + + debug!("add_liveness_constraints()"); + 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); + + 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); + }) + } + }); + } + } +} diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index fc42fd42b15..273972f6937 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -8,215 +8,115 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use self::infer::InferenceContext; -use rustc::ty::TypeFoldable; -use rustc::ty::subst::{Kind, Substs}; -use rustc::ty::{Ty, TyCtxt, ClosureSubsts, RegionVid, RegionKind}; -use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind}; -use rustc::mir::visit::{MutVisitor, Lookup}; +use rustc::ty::{self, RegionKind, TyCtxt}; +use rustc::mir::{Location, Mir}; use rustc::mir::transform::{MirPass, MirSource}; -use rustc::infer::{self as rustc_infer, InferCtxt}; -use rustc::util::nodemap::{FxHashMap, FxHashSet}; -use rustc_data_structures::indexed_vec::{IndexVec, Idx}; -use syntax_pos::DUMMY_SP; -use std::collections::HashMap; +use rustc::infer::InferCtxt; +use rustc::util::nodemap::FxHashMap; +use rustc_data_structures::indexed_vec::Idx; +use std::collections::BTreeSet; use std::fmt; -use util::liveness; +use util::liveness::{self, LivenessResult}; use util as mir_util; use self::mir_util::PassWhere; -mod infer; +mod constraint_generation; -#[allow(dead_code)] -struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - lookup_map: HashMap, - regions: IndexVec, - #[allow(dead_code)] - infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, -} +mod region_infer; +use self::region_infer::RegionInferenceContext; -impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self { - NLLVisitor { - infcx, - lookup_map: HashMap::new(), - regions: IndexVec::new(), - } - } - - pub fn into_results(self) -> (HashMap, IndexVec) { - (self.lookup_map, self.regions) - } - - fn renumber_regions(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> { - self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| { - self.regions.push(Region::default()); - self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) - }) - } - - fn store_region(&mut self, region: &RegionKind, lookup: Lookup) { - if let RegionKind::ReVar(rid) = *region { - self.lookup_map.entry(rid).or_insert(lookup); - } - } - - fn store_ty_regions(&mut self, ty: &Ty<'tcx>, lookup: Lookup) { - for region in ty.regions() { - self.store_region(region, lookup); - } - } - - fn store_kind_regions(&mut self, kind: &'tcx Kind, lookup: Lookup) { - if let Some(ty) = kind.as_type() { - self.store_ty_regions(&ty, lookup); - } else if let Some(region) = kind.as_region() { - self.store_region(region, lookup); - } - } -} - -impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { - fn visit_ty(&mut self, ty: &mut Ty<'tcx>, lookup: Lookup) { - let old_ty = *ty; - *ty = self.renumber_regions(&old_ty); - self.store_ty_regions(ty, lookup); - } - - fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) { - *substs = self.renumber_regions(&{*substs}); - let lookup = Lookup::Loc(location); - for kind in *substs { - self.store_kind_regions(kind, lookup); - } - } - - fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { - match *rvalue { - Rvalue::Ref(ref mut r, _, _) => { - let old_r = *r; - *r = self.renumber_regions(&old_r); - let lookup = Lookup::Loc(location); - self.store_region(r, lookup); - } - Rvalue::Use(..) | - Rvalue::Repeat(..) | - Rvalue::Len(..) | - Rvalue::Cast(..) | - Rvalue::BinaryOp(..) | - Rvalue::CheckedBinaryOp(..) | - Rvalue::UnaryOp(..) | - Rvalue::Discriminant(..) | - Rvalue::NullaryOp(..) | - Rvalue::Aggregate(..) => { - // These variants don't contain regions. - } - } - self.super_rvalue(rvalue, location); - } - - fn visit_closure_substs(&mut self, - substs: &mut ClosureSubsts<'tcx>, - location: Location) { - *substs = self.renumber_regions(substs); - let lookup = Lookup::Loc(location); - for kind in substs.substs { - self.store_kind_regions(kind, lookup); - } - } - - fn visit_statement(&mut self, - block: BasicBlock, - statement: &mut Statement<'tcx>, - location: Location) { - if let StatementKind::EndRegion(_) = statement.kind { - statement.kind = StatementKind::Nop; - } - self.super_statement(block, statement, location); - } -} +mod renumber; // MIR Pass for non-lexical lifetimes pub struct NLL; impl MirPass for NLL { - fn run_pass<'a, 'tcx>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - source: MirSource, - input_mir: &mut Mir<'tcx>) { + fn run_pass<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + input_mir: &mut Mir<'tcx>, + ) { if !tcx.sess.opts.debugging_opts.nll { return; } - tcx.infer_ctxt().enter(|infcx| { + tcx.infer_ctxt().enter(|ref infcx| { // Clone mir so we can mutate it without disturbing the rest of the compiler let mir = &mut input_mir.clone(); - let mut visitor = NLLVisitor::new(&infcx); - visitor.visit_mir(mir); + // Replace all regions with fresh inference variables. + let num_region_variables = renumber::renumber_mir(infcx, mir); - let liveness = liveness::liveness_of_locals(mir); + // Compute what is live where. + let liveness = &liveness::liveness_of_locals(mir); - let liveness_per_location: FxHashMap<_, _> = - mir - .basic_blocks() - .indices() - .flat_map(|bb| { - let mut results = vec![]; - liveness.simulate_block(&mir, bb, |location, local_set| { - results.push((location, local_set.clone())); - }); - results - }) - .collect(); + // Create the region inference context, generate the constraints, + // and then solve them. + let regioncx = &mut RegionInferenceContext::new(num_region_variables); + constraint_generation::generate_constraints(infcx, regioncx, mir, liveness); + regioncx.solve(infcx, mir); - mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| { - match pass_where { - // Before the CFG, dump out the values for each region variable. - PassWhere::BeforeCFG => { - for (index, value) in visitor.regions.iter_enumerated() { - writeln!(out, "| R{:03}: {:?}", index.0, value)?; - } - } - - // 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)?; - } - } - - PassWhere::InCFG(location) => { - let local_set = &liveness_per_location[&location]; - let mut string = String::new(); - for local in local_set.iter() { - string.push_str(&format!(", {:?}", local)); - } - if !string.is_empty() { - writeln!(out, " | Live variables here: [{}]", &string[2..])?; - } else { - writeln!(out, " | Live variables here: []")?; - } - } - - PassWhere::AfterCFG => { } - } - Ok(()) - }); - let (_lookup_map, regions) = visitor.into_results(); - let mut inference_context = InferenceContext::new(regions); - inference_context.solve(&infcx, mir); + // Dump MIR results into a file, if that is enabled. + dump_mir_results(infcx, liveness, source, regioncx, mir); }) } } +fn dump_mir_results<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + liveness: &LivenessResult, + source: MirSource, + regioncx: &RegionInferenceContext, + mir: &Mir<'tcx>, +) { + if !mir_util::dump_enabled(infcx.tcx, "nll", source) { + return; + } + + let liveness_per_location: FxHashMap<_, _> = mir.basic_blocks() + .indices() + .flat_map(|bb| { + let mut results = vec![]; + liveness.simulate_block(&mir, bb, |location, local_set| { + results.push((location, local_set.clone())); + }); + results + }) + .collect(); + + mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| { + match pass_where { + // Before the CFG, dump out the values for each region variable. + PassWhere::BeforeCFG => for region in regioncx.regions() { + writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?; + }, + + // 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)?; + } + } + + PassWhere::InCFG(location) => { + let local_set = &liveness_per_location[&location]; + writeln!(out, " | Live variables here: {:?}", local_set)?; + } + + PassWhere::AfterCFG => {} + } + Ok(()) + }); +} + #[derive(Clone, Default, PartialEq, Eq)] pub struct Region { - points: FxHashSet, + points: BTreeSet, } impl fmt::Debug for Region { @@ -235,4 +135,25 @@ impl Region { } } -newtype_index!(RegionIndex); +newtype_index!(RegionIndex { + DEBUG_NAME = "R", +}); + +/// Right now, we piggy back on the `ReVar` to store our NLL inference +/// regions. These are indexed with `RegionIndex`. This method will +/// assert that the region is a `ReVar` and convert the internal index +/// into a `RegionIndex`. This is reasonable because in our MIR we +/// replace all free regions with inference variables. +trait ToRegionIndex { + fn to_region_index(&self) -> RegionIndex; +} + +impl ToRegionIndex for RegionKind { + fn to_region_index(&self) -> RegionIndex { + if let &ty::ReVar(vid) = self { + RegionIndex::new(vid.index as usize) + } else { + bug!("region is not an ReVar: {:?}", self) + } + } +} diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/region_infer.rs similarity index 85% rename from src/librustc_mir/transform/nll/infer.rs rename to src/librustc_mir/transform/nll/region_infer.rs index e6e00f295ca..75abd4d3ff5 100644 --- a/src/librustc_mir/transform/nll/infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -15,7 +15,7 @@ use rustc::mir::{Location, Mir}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc_data_structures::fx::FxHashSet; -pub struct InferenceContext { +pub struct RegionInferenceContext { definitions: IndexVec, constraints: IndexVec, errors: IndexVec, @@ -28,22 +28,13 @@ pub struct InferenceError { newtype_index!(InferenceErrorIndex); +#[derive(Default)] struct VarDefinition { name: (), // FIXME(nashenas88) RegionName value: Region, capped: bool, } -impl VarDefinition { - pub fn new(value: Region) -> Self { - Self { - name: (), - value, - capped: false, - } - } -} - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct Constraint { sub: RegionIndex, @@ -53,10 +44,12 @@ pub struct Constraint { newtype_index!(ConstraintIndex); -impl InferenceContext { - pub fn new(values: IndexVec) -> Self { +impl RegionInferenceContext { + pub fn new(num_region_variables: usize) -> Self { Self { - definitions: values.into_iter().map(VarDefinition::new).collect(), + definitions: (0..num_region_variables) + .map(|_| VarDefinition::default()) + .collect(), constraints: IndexVec::new(), errors: IndexVec::new(), } @@ -87,8 +80,14 @@ impl InferenceContext { self.constraints.push(Constraint { sup, sub, point }); } - #[allow(dead_code)] - pub fn region(&self, v: RegionIndex) -> &Region { + /// Returns an iterator over all the region indices. + pub fn regions(&self) -> impl Iterator { + self.definitions.indices() + } + + /// Returns the current value for the region `v`. This is only + /// really meaningful after `solve` has executed. + pub fn region_value(&self, v: RegionIndex) -> &Region { &self.definitions[v].value } @@ -144,8 +143,7 @@ impl InferenceContext { } struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> { - #[allow(dead_code)] - infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + #[allow(dead_code)] infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, } @@ -183,17 +181,22 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> { let block_data = &self.mir[p.block]; let successor_points = if p.statement_index < block_data.statements.len() { - vec![Location { - statement_index: p.statement_index + 1, - ..p - }] + vec![ + Location { + statement_index: p.statement_index + 1, + ..p + }, + ] } else { - block_data.terminator() + block_data + .terminator() .successors() .iter() - .map(|&basic_block| Location { - statement_index: 0, - block: basic_block, + .map(|&basic_block| { + Location { + statement_index: 0, + block: basic_block, + } }) .collect::>() }; diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs new file mode 100644 index 00000000000..589179c2066 --- /dev/null +++ b/src/librustc_mir/transform/nll/renumber.rs @@ -0,0 +1,132 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::ty::TypeFoldable; +use rustc::ty::subst::{Kind, Substs}; +use rustc::ty::{Ty, ClosureSubsts, RegionVid, RegionKind}; +use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind}; +use rustc::mir::visit::{MutVisitor, Lookup}; +use rustc::infer::{self as rustc_infer, InferCtxt}; +use syntax_pos::DUMMY_SP; +use std::collections::HashMap; + +/// Replaces all free regions appearing in the MIR with fresh +/// inference variables, returning the number of variables created. +pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, + mir: &mut Mir<'tcx>) + -> usize +{ + let mut visitor = NLLVisitor::new(infcx); + visitor.visit_mir(mir); + visitor.num_region_variables +} + +struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { + lookup_map: HashMap, + num_region_variables: usize, + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, +} + +impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { + pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self { + NLLVisitor { + infcx, + lookup_map: HashMap::new(), + num_region_variables: 0 + } + } + + fn renumber_regions(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> { + self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| { + self.num_region_variables += 1; + self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) + }) + } + + fn store_region(&mut self, region: &RegionKind, lookup: Lookup) { + if let RegionKind::ReVar(rid) = *region { + self.lookup_map.entry(rid).or_insert(lookup); + } + } + + fn store_ty_regions(&mut self, ty: &Ty<'tcx>, lookup: Lookup) { + for region in ty.regions() { + self.store_region(region, lookup); + } + } + + fn store_kind_regions(&mut self, kind: &'tcx Kind, lookup: Lookup) { + if let Some(ty) = kind.as_type() { + self.store_ty_regions(&ty, lookup); + } else if let Some(region) = kind.as_region() { + self.store_region(region, lookup); + } + } +} + +impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { + fn visit_ty(&mut self, ty: &mut Ty<'tcx>, lookup: Lookup) { + let old_ty = *ty; + *ty = self.renumber_regions(&old_ty); + self.store_ty_regions(ty, lookup); + } + + fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) { + *substs = self.renumber_regions(&{*substs}); + let lookup = Lookup::Loc(location); + for kind in *substs { + self.store_kind_regions(kind, lookup); + } + } + + fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { + match *rvalue { + Rvalue::Ref(ref mut r, _, _) => { + let old_r = *r; + *r = self.renumber_regions(&old_r); + let lookup = Lookup::Loc(location); + self.store_region(r, lookup); + } + Rvalue::Use(..) | + Rvalue::Repeat(..) | + Rvalue::Len(..) | + Rvalue::Cast(..) | + Rvalue::BinaryOp(..) | + Rvalue::CheckedBinaryOp(..) | + Rvalue::UnaryOp(..) | + Rvalue::Discriminant(..) | + Rvalue::NullaryOp(..) | + Rvalue::Aggregate(..) => { + // These variants don't contain regions. + } + } + self.super_rvalue(rvalue, location); + } + + fn visit_closure_substs(&mut self, + substs: &mut ClosureSubsts<'tcx>, + location: Location) { + *substs = self.renumber_regions(substs); + let lookup = Lookup::Loc(location); + for kind in substs.substs { + self.store_kind_regions(kind, lookup); + } + } + + fn visit_statement(&mut self, + block: BasicBlock, + statement: &mut Statement<'tcx>, + location: Location) { + if let StatementKind::EndRegion(_) = statement.kind { + statement.kind = StatementKind::Nop; + } + self.super_statement(block, statement, location); + } +} diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs new file mode 100644 index 00000000000..d1ef08cf00d --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -0,0 +1,55 @@ +// 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 or the MIT license +// , 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)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let p = &v[0]; + if true { + use_x(*p); + } else { + use_x(22); + } +} + +// END RUST SOURCE +// START rustc.node12.nll.0.mir +// | R1: {bb1[1], bb2[0], bb2[1]} +// ... +// let _2: &'_#1r usize; +// END rustc.node12.nll.0.mir +// START rustc.node12.nll.0.mir +// bb1: { +// | Live variables here: [_1, _3] +// _2 = &'_#0r _1[_3]; +// | Live variables here: [_2] +// switchInt(const true) -> [0u8: bb3, otherwise: bb2]; +// } +// END rustc.node12.nll.0.mir +// START rustc.node12.nll.0.mir +// bb2: { +// | Live variables here: [_2] +// StorageLive(_7); +// | Live variables here: [_2] +// _7 = (*_2); +// | Live variables here: [_7] +// _6 = const use_x(_7) -> bb4; +// } +// END rustc.node12.nll.0.mir From 899c7ad9b2003482283615902fb29513e2fc22ac Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 15:37:47 -0400 Subject: [PATCH 10/33] avoid unnecessary copies in liveness computation --- src/librustc_mir/util/liveness.rs | 41 +++++++++++++------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index 7e4529e9b5e..df7ba3c62fa 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -171,40 +171,33 @@ pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult { block(b, locals) }).collect(); - let copy = |from: &IndexVec, to: &mut IndexVec| { - for (i, set) in to.iter_enumerated_mut() { - set.clone_from(&from[i]); - } - }; - let mut ins: IndexVec<_, _> = mir.basic_blocks() .indices() - .map(|_| LocalSet::new_empty(locals)).collect(); + .map(|_| LocalSet::new_empty(locals)) + .collect(); let mut outs = ins.clone(); - let mut ins_ = ins.clone(); - let mut outs_ = outs.clone(); - - loop { - copy(&ins, &mut ins_); - copy(&outs, &mut outs_); + let mut changed = true; + let mut bits = LocalSet::new_empty(locals); + while changed { + changed = false; for b in mir.basic_blocks().indices().rev() { - // out = ∪ {ins of successors} - outs[b].clear(); + // outs[b] = ∪ {ins of successors} + bits.clear(); for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() { - outs[b].union(&ins[successor]); + bits.union(&ins[successor]); } + outs[b].clone_from(&bits); - // in = use ∪ (out - def) - ins[b].clone_from(&outs[b]); + // bits = use ∪ (bits - def) + def_use[b].apply(&mut bits); - // FIXME use the return value to detect if we have changed things - def_use[b].apply(&mut ins[b]); - } - - if ins_ == ins && outs_ == outs { - break; + // update bits on entry and flag if they have changed + if ins[b] != bits { + ins[b].clone_from(&bits); + changed = true; + } } } From cb56ff5a775a8915d2e79cd8c8a0f2992f1a561d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 15:39:08 -0400 Subject: [PATCH 11/33] reorder liveness to bring the more significant code up top --- src/librustc_mir/util/liveness.rs | 214 +++++++++++++++--------------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index df7ba3c62fa..514ff2ab830 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -47,6 +47,113 @@ use std::io::{self, Write}; pub type LocalSet = IdxSetBuf; +// This gives the result of the liveness analysis at the boundary of basic blocks +pub struct LivenessResult { + pub ins: IndexVec, + pub outs: IndexVec, +} + +pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult { + let locals = mir.local_decls.len(); + let def_use: IndexVec<_, _> = mir.basic_blocks().iter().map(|b| { + block(b, locals) + }).collect(); + + let mut ins: IndexVec<_, _> = mir.basic_blocks() + .indices() + .map(|_| LocalSet::new_empty(locals)) + .collect(); + let mut outs = ins.clone(); + + let mut changed = true; + let mut bits = LocalSet::new_empty(locals); + while changed { + changed = false; + + for b in mir.basic_blocks().indices().rev() { + // outs[b] = ∪ {ins of successors} + bits.clear(); + for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() { + bits.union(&ins[successor]); + } + outs[b].clone_from(&bits); + + // bits = use ∪ (bits - def) + def_use[b].apply(&mut bits); + + // update bits on entry and flag if they have changed + if ins[b] != bits { + ins[b].clone_from(&bits); + changed = true; + } + } + } + + LivenessResult { + ins, + outs, + } +} + +impl LivenessResult { + /// Walks backwards through the statements/terminator in the given + /// 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) + { + let data = &mir[block]; + + // Get a copy of the bits on exit from the block. + let mut bits = self.outs[block].clone(); + + // Start with the maximal statement index -- i.e., right before + // the terminator executes. + let mut statement_index = data.statements.len(); + + // Compute liveness right before terminator and invoke callback. + 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); + + // 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_defs_uses = self.defs_uses(mir, statement_location, statement); + statement_defs_uses.apply(&mut bits); + callback(statement_location, &bits); + } + + assert_eq!(bits, self.ins[block]); + } + + 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), + }; + + // 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 + } +} + #[derive(Eq, PartialEq, Clone)] struct DefsUses { defs: LocalSet, @@ -159,113 +266,6 @@ fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses { visitor } -// This gives the result of the liveness analysis at the boundary of basic blocks -pub struct LivenessResult { - pub ins: IndexVec, - pub outs: IndexVec, -} - -pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult { - let locals = mir.local_decls.len(); - let def_use: IndexVec<_, _> = mir.basic_blocks().iter().map(|b| { - block(b, locals) - }).collect(); - - let mut ins: IndexVec<_, _> = mir.basic_blocks() - .indices() - .map(|_| LocalSet::new_empty(locals)) - .collect(); - let mut outs = ins.clone(); - - let mut changed = true; - let mut bits = LocalSet::new_empty(locals); - while changed { - changed = false; - - for b in mir.basic_blocks().indices().rev() { - // outs[b] = ∪ {ins of successors} - bits.clear(); - for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() { - bits.union(&ins[successor]); - } - outs[b].clone_from(&bits); - - // bits = use ∪ (bits - def) - def_use[b].apply(&mut bits); - - // update bits on entry and flag if they have changed - if ins[b] != bits { - ins[b].clone_from(&bits); - changed = true; - } - } - } - - LivenessResult { - ins, - outs, - } -} - -impl LivenessResult { - /// Walks backwards through the statements/terminator in the given - /// 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) - { - let data = &mir[block]; - - // Get a copy of the bits on exit from the block. - let mut bits = self.outs[block].clone(); - - // Start with the maximal statement index -- i.e., right before - // the terminator executes. - let mut statement_index = data.statements.len(); - - // Compute liveness right before terminator and invoke callback. - 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); - - // 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_defs_uses = self.defs_uses(mir, statement_location, statement); - statement_defs_uses.apply(&mut bits); - callback(statement_location, &bits); - } - - assert_eq!(bits, self.ins[block]); - } - - 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), - }; - - // 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 - } -} - trait MirVisitable<'tcx> { fn apply(&self, location: Location, visitor: &mut V) where V: Visitor<'tcx>; From e02937848b9316d393577171ede62a3423132583 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 16:20:47 -0400 Subject: [PATCH 12/33] extend liveness to distinguish "drop" and "non-drop" uses --- src/librustc_mir/transform/generator.rs | 7 +- .../transform/nll/constraint_generation.rs | 49 ++-- src/librustc_mir/transform/nll/mod.rs | 51 +++- src/librustc_mir/util/liveness.rs | 220 +++++++++++------- .../mir-opt/nll/liveness-call-subtlety.rs | 19 +- .../mir-opt/nll/liveness-drop-intra-block.rs | 18 +- src/test/mir-opt/nll/liveness-interblock.rs | 19 +- src/test/mir-opt/nll/region-liveness-basic.rs | 15 +- .../nll/region-liveness-drop-may-dangle.rs | 48 ++++ 9 files changed, 307 insertions(+), 139 deletions(-) create mode 100644 src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 7d0814b67fb..52a50333f45 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -68,7 +68,7 @@ use rustc::mir::visit::{LvalueContext, Visitor, MutVisitor}; 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(); diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs index 1c0c3b6fc33..518e140b5dd 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -10,24 +10,30 @@ 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 @@ impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> { 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); + }) + } + }); } } } diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 273972f6937..131f088d91c 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -16,7 +16,7 @@ use rustc::util::nodemap::FxHashMap; 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 @@ impl MirPass for NLL { 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 @@ impl MirPass for NLL { } } +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.simulate_block(&mir, bb, |location, local_set| { + 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.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 = ®ular_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 => {} diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index 514ff2ab830..7658e49ea5e 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -35,29 +35,60 @@ 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; -// 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, + + /// Live variables on exit to each basic block. This is equal to + /// the union of the `ins` for each successor. pub outs: IndexVec, } -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 @@ impl LivenessResult { 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 @@ impl LivenessResult { // 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 @@ impl LivenessResult { 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 @@ impl DefsUses { } } -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 @@ impl<'tcx> Visitor<'tcx> for DefsUses { // 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 @@ impl<'tcx> Visitor<'tcx> for DefsUses { 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(&self, location: Location, visitor: &mut V) - where V: Visitor<'tcx>; + where + V: Visitor<'tcx>; } impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> { fn apply(&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> { fn apply(&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| { - let live: Vec = mir.local_decls.indices() + let live: Vec = 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(()) } - diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs index f0d56db0573..e9af37a1f64 100644 --- a/src/test/mir-opt/nll/liveness-call-subtlety.rs +++ b/src/test/mir-opt/nll/liveness-call-subtlety.rs @@ -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 >::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 diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs index 1fac9484bdb..957a57428e3 100644 --- a/src/test/mir-opt/nll/liveness-drop-intra-block.rs +++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs @@ -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 diff --git a/src/test/mir-opt/nll/liveness-interblock.rs b/src/test/mir-opt/nll/liveness-interblock.rs index 4380698e704..f5a2a25a9b0 100644 --- a/src/test/mir-opt/nll/liveness-interblock.rs +++ b/src/test/mir-opt/nll/liveness-interblock.rs @@ -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 diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index d1ef08cf00d..67e16c2fe6f 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -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 index 00000000000..7482288e911 --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs @@ -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 or the MIT license +// , 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 { + value: T +} + +unsafe impl<#[may_dangle] T> Drop for Wrap { + 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 From af09f720d60c1226b5d42aaee3cc0c0e67ebe5cc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 17:14:39 -0400 Subject: [PATCH 13/33] preliminary support for may-dangle attribute and drop constraints --- .../transform/nll/constraint_generation.rs | 126 ++++++++++++++++-- src/librustc_mir/transform/nll/mod.rs | 2 +- .../nll/region-liveness-drop-no-may-dangle.rs | 50 +++++++ 3 files changed, 164 insertions(+), 14 deletions(-) create mode 100644 src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs index 518e140b5dd..1e008ec38f2 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -8,8 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::mir::Mir; +use rustc::mir::{Location, Mir}; +use rustc::mir::transform::MirSource; use rustc::infer::InferCtxt; +use rustc::traits::{self, ObligationCause}; +use rustc::ty::{self, Ty}; +use rustc::ty::fold::TypeFoldable; +use rustc::util::common::ErrorReported; +use rustc_data_structures::fx::FxHashSet; +use syntax::codemap::DUMMY_SP; use super::LivenessResults; use super::ToRegionIndex; @@ -19,6 +26,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, regioncx: &mut RegionInferenceContext, mir: &Mir<'tcx>, + mir_source: MirSource, liveness: &LivenessResults, ) { ConstraintGeneration { @@ -26,6 +34,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( regioncx, mir, liveness, + mir_source, }.add_constraints(); } @@ -34,6 +43,7 @@ struct ConstraintGeneration<'constrain, 'gcx: 'tcx, 'tcx: 'constrain> { regioncx: &'constrain mut RegionInferenceContext, mir: &'constrain Mir<'tcx>, liveness: &'constrain LivenessResults, + mir_source: MirSource, } impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> { @@ -47,8 +57,6 @@ impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> { /// > If a variable V is live at point P, then all regions R in the type of V /// > must include the point P. fn add_liveness_constraints(&mut self) { - let tcx = self.infcx.tcx; - debug!("add_liveness_constraints()"); for bb in self.mir.basic_blocks().indices() { debug!("add_liveness_constraints: bb={:?}", bb); @@ -56,20 +64,112 @@ impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> { 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); - }) + self.add_regular_live_constraint(live_local_ty, location); + } + }); + + self.liveness + .drop + .simulate_block(self.mir, bb, |location, live_locals| { + for live_local in live_locals.iter() { + let live_local_ty = self.mir.local_decls[live_local].ty; + self.add_drop_live_constraint(live_local_ty, location); } }); } } + + /// Some variable with type `live_ty` is "regular live" at + /// `location` -- i.e., it may be used later. This means that all + /// regions appearing in the type `live_ty` must be live at + /// `location`. + fn add_regular_live_constraint(&mut self, live_ty: T, location: Location) + where + T: TypeFoldable<'tcx>, + { + debug!( + "add_regular_live_constraint(live_ty={:?}, location={:?})", + live_ty, + location + ); + + self.infcx + .tcx + .for_each_free_region(&live_ty, |live_region| { + let vid = live_region.to_region_index(); + self.regioncx.add_live_point(vid, location); + }); + } + + /// Some variable with type `live_ty` is "drop live" at `location` + /// -- i.e., it may be dropped later. This means that *some* of + /// the regions in its type must be live at `location`. The + /// precise set will depend on the dropck constraints, and in + /// particular this takes `#[may_dangle]` into account. + fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) { + debug!( + "add_drop_live_constraint(dropped_ty={:?}, location={:?})", + dropped_ty, + location + ); + + let tcx = self.infcx.tcx; + let mut types = vec![(dropped_ty, 0)]; + let mut known = FxHashSet(); + while let Some((ty, depth)) = types.pop() { + let span = DUMMY_SP; // FIXME + let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) { + Ok(result) => result, + Err(ErrorReported) => { + continue; + } + }; + + let ty::DtorckConstraint { + outlives, + dtorck_types, + } = result; + + // All things in the `outlives` array may be touched by + // the destructor and must be live at this point. + for outlive in outlives { + if let Some(ty) = outlive.as_type() { + self.add_regular_live_constraint(ty, location); + } else if let Some(r) = outlive.as_region() { + self.add_regular_live_constraint(r, location); + } else { + bug!() + } + } + + // However, there may also be some types that + // `dtorck_constraint_for_ty` could not resolve (e.g., + // associated types and parameters). We need to normalize + // associated types here and possibly recursively process. + let def_id = tcx.hir.local_def_id(self.mir_source.item_id()); + let param_env = self.infcx.tcx.param_env(def_id); + for ty in dtorck_types { + // FIXME -- I think that this may disregard some region obligations + // or something. Do we care? -nmatsakis + let cause = ObligationCause::dummy(); + match traits::fully_normalize(self.infcx, cause, param_env, &ty) { + Ok(ty) => match ty.sty { + ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => { + self.add_regular_live_constraint(ty, location); + } + + _ => if known.insert(ty) { + types.push((ty, depth + 1)); + }, + }, + + Err(errors) => { + self.infcx.report_fulfillment_errors(&errors, None); + } + } + } + } + } } diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 131f088d91c..6139be69566 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -65,7 +65,7 @@ impl MirPass for NLL { // Create the region inference context, generate the constraints, // and then solve them. let regioncx = &mut RegionInferenceContext::new(num_region_variables); - constraint_generation::generate_constraints(infcx, regioncx, mir, liveness); + constraint_generation::generate_constraints(infcx, regioncx, mir, source, liveness); regioncx.solve(infcx, mir); // Dump MIR results into a file, if that is enabled. diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs new file mode 100644 index 00000000000..5f4da966281 --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -0,0 +1,50 @@ +// 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 or the MIT license +// , 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. + +// ignore-tidy-linelength +// compile-flags:-Znll -Zverbose +// ^^^^^^^^^ force compiler to dump more region information + +#![allow(warnings)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let p: Wrap<& /* R1 */ usize> = Wrap { value: &v[0] }; + if true { + use_x(*p.value); + } else { + use_x(22); + } + + // `p` will get dropped here. Because the `#[may_dangle]` + // attribute is not present on `Wrap`, we must conservatively + // assume that the dtor may access the `value` field, and hence we + // must consider R1 to be live. +} + +struct Wrap { + value: T +} + +// Look ma, no `#[may_dangle]` attribute here. +impl Drop for Wrap { + fn drop(&mut self) { } +} + +// END RUST SOURCE +// START rustc.node12.nll.0.mir +// | R4: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb8[0]} +// END rustc.node12.nll.0.mir From 24442ffa666925b7c98fb9e9197811700bc36d26 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Oct 2017 18:28:39 -0400 Subject: [PATCH 14/33] add subregion between borrow region and resulting reference --- src/librustc/mir/mod.rs | 8 +++ .../transform/nll/constraint_generation.rs | 63 ++++++++++++++++--- src/test/mir-opt/nll/region-liveness-basic.rs | 1 + .../nll/region-liveness-two-disjoint-uses.rs | 49 +++++++++++++++ 4 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index f5a3c1989cf..307637b2f1d 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1637,6 +1637,14 @@ impl fmt::Debug for Location { } impl Location { + /// Returns the location immediately after this one within the enclosing block. + /// + /// Note that if this location represents a terminator, then the + /// resulting location would be out of bounds and invalid. + pub fn successor_within_block(&self) -> Location { + Location { block: self.block, statement_index: self.statement_index + 1 } + } + pub fn dominates(&self, other: &Location, dominators: &Dominators) -> bool { if self.block == other.block { self.statement_index <= other.statement_index diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs index 1e008ec38f2..1acbd72a47d 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -8,8 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::mir::{Location, Mir}; +use rustc::mir::{BasicBlock, BorrowKind, Location, Lvalue, Mir, Rvalue, Statement, StatementKind}; use rustc::mir::transform::MirSource; +use rustc::mir::visit::Visitor; use rustc::infer::InferCtxt; use rustc::traits::{self, ObligationCause}; use rustc::ty::{self, Ty}; @@ -38,18 +39,18 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( }.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 LivenessResults, +struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + regioncx: &'cx mut RegionInferenceContext, + mir: &'cx Mir<'tcx>, + liveness: &'cx LivenessResults, mir_source: MirSource, } -impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { fn add_constraints(&mut self) { - // To start, add the liveness constraints. self.add_liveness_constraints(); + self.add_borrow_constraints(); } /// Liveness constraints: @@ -172,4 +173,50 @@ impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> { } } } + + fn add_borrow_constraints(&mut self) { + self.visit_mir(self.mir); + } + + fn add_borrow_constraint( + &mut self, + location: Location, + destination_lv: &Lvalue<'tcx>, + borrow_region: ty::Region<'tcx>, + _borrow_kind: BorrowKind, + _borrowed_lv: &Lvalue<'tcx>, + ) { + let tcx = self.infcx.tcx; + let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx); + + let destination_region = match destination_ty.sty { + ty::TyRef(r, _) => r, + _ => bug!() + }; + + self.regioncx.add_outlives(borrow_region.to_region_index(), + destination_region.to_region_index(), + location.successor_within_block()); + } +} + +impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> { + fn visit_statement(&mut self, + block: BasicBlock, + statement: &Statement<'tcx>, + location: Location) { + // Look for a statement like: + // + // D = & L + // + // where D is the path to which we are assigning, and + // L is the path that is borrowed. + if let StatementKind::Assign(ref destination_lv, ref rv) = statement.kind { + if let Rvalue::Ref(region, bk, ref borrowed_lv) = *rv { + self.add_borrow_constraint(location, destination_lv, region, bk, borrowed_lv); + } + } + + self.super_statement(block, statement, location); + } } diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index 67e16c2fe6f..3ab83a8eec3 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -31,6 +31,7 @@ fn main() { // END RUST SOURCE // START rustc.node12.nll.0.mir +// | R0: {bb1[1], bb2[0], bb2[1]} // | R1: {bb1[1], bb2[0], bb2[1]} // ... // let _2: &'_#1r usize; diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs new file mode 100644 index 00000000000..eb904af39ac --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -0,0 +1,49 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test for the subregion constraints. In this case, the region R3 on +// `p` includes two disjoint regions of the control-flow graph. The +// borrows in `&v[0]` and `&v[1]` each (in theory) have to outlive R3, +// but only at a particular point, and hence they wind up including +// distinct regions. + +// compile-flags:-Znll -Zverbose +// ^^^^^^^^^ force compiler to dump more region information + +#![allow(warnings)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let mut p = &v[0]; + if true { + use_x(*p); + } else { + use_x(22); + } + + p = &v[1]; + use_x(*p); +} + +// END RUST SOURCE +// START rustc.node12.nll.0.mir +// | R0: {bb1[1], bb2[0], bb2[1]} +// ... +// | R2: {bb7[2], bb7[3], bb7[4]} +// | R3: {bb1[1], bb2[0], bb2[1], bb7[2], bb7[3], bb7[4]} +// ... +// let mut _2: &'_#3r usize; +// ... +// _2 = &'_#0r _1[_3]; +// ... +// _2 = &'_#2r (*_10); +// END rustc.node12.nll.0.mir From 74140603444716b257a060c3c467295d580c9afc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 25 Oct 2017 10:59:50 -0400 Subject: [PATCH 15/33] update the format of liveness debug dumps to be more readable --- src/librustc_mir/transform/nll/mod.rs | 83 ++++++++++++++----- .../mir-opt/nll/liveness-call-subtlety.rs | 18 ++-- .../mir-opt/nll/liveness-drop-intra-block.rs | 18 ++-- src/test/mir-opt/nll/liveness-interblock.rs | 18 ++-- src/test/mir-opt/nll/region-liveness-basic.rs | 15 ++-- 5 files changed, 83 insertions(+), 69 deletions(-) diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 6139be69566..1498a6b23c0 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -16,7 +16,7 @@ use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_vec::Idx; use std::collections::BTreeSet; use std::fmt; -use util::liveness::{self, LivenessResult, LivenessMode}; +use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; use util as mir_util; use self::mir_util::PassWhere; @@ -51,15 +51,21 @@ impl MirPass for NLL { // Compute what is live where. let liveness = &LivenessResults { - regular: liveness::liveness_of_locals(mir, LivenessMode { - include_regular_use: true, - include_drops: false, - }), + 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, - }) + drop: liveness::liveness_of_locals( + mir, + LivenessMode { + include_regular_use: false, + include_drops: true, + }, + ), }; // Create the region inference context, generate the constraints, @@ -94,9 +100,11 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( .indices() .flat_map(|bb| { let mut results = vec![]; - liveness.regular.simulate_block(&mir, bb, |location, local_set| { - results.push((location, local_set.clone())); - }); + liveness + .regular + .simulate_block(&mir, bb, |location, local_set| { + results.push((location, local_set.clone())); + }); results }) .collect(); @@ -105,9 +113,11 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( .indices() .flat_map(|bb| { let mut results = vec![]; - liveness.drop.simulate_block(&mir, bb, |location, local_set| { - results.push((location, local_set.clone())); - }); + liveness + .drop + .simulate_block(&mir, bb, |location, local_set| { + results.push((location, local_set.clone())); + }); results }) .collect(); @@ -122,17 +132,21 @@ 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) => { - 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])?; + let s = live_variable_set(&liveness.regular.ins[bb], &liveness.drop.ins[bb]); + writeln!(out, " | Live variables on entry to {:?}: {}", bb, s)?; } PassWhere::InCFG(location) => { - let local_set = ®ular_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)?; + let s = live_variable_set( + ®ular_liveness_per_location[&location], + &drop_liveness_per_location[&location], + ); + writeln!( + out, + " | Live variables at {:?}: {}", + location, + s + )?; } PassWhere::AfterCFG => {} @@ -184,3 +198,26 @@ impl ToRegionIndex for RegionKind { } } } + +fn live_variable_set(regular: &LocalSet, drops: &LocalSet) -> String { + // sort and deduplicate: + let all_locals: BTreeSet<_> = regular.iter().chain(drops.iter()).collect(); + + // construct a string with each local, including `(drop)` if it is + // only dropped, versus a regular use. + let mut string = String::new(); + for local in all_locals { + string.push_str(&format!("{:?}", local)); + + if !regular.contains(&local) { + assert!(drops.contains(&local)); + string.push_str(" (drop)"); + } + + string.push_str(", "); + } + + let len = if string.is_empty() { 0 } else { string.len() - 2 }; + + format!("[{}]", &string[..len]) +} diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs index e9af37a1f64..551aff8454a 100644 --- a/src/test/mir-opt/nll/liveness-call-subtlety.rs +++ b/src/test/mir-opt/nll/liveness-call-subtlety.rs @@ -26,26 +26,20 @@ fn main() { // // END RUST SOURCE // START rustc.node12.nll.0.mir -// | Variables regular-live on entry to the block bb0: [] -// | Variables drop-live on entry to the block bb0: [] +// | Live variables on entry to bb0: [] // bb0: { -// | Regular-Live variables here: [] -// | Drop-Live variables here: [] +// | Live variables at bb0[0]: [] // StorageLive(_1); -// | Regular-Live variables here: [] -// | Drop-Live variables here: [] +// | Live variables at bb0[1]: [] // _1 = const >::new(const 22usize) -> bb1; // } // END rustc.node12.nll.0.mir // START rustc.node12.nll.0.mir -// | Variables regular-live on entry to the block bb1: [] -// | Variables drop-live on entry to the block bb1: [_1] +// | Live variables on entry to bb1: [_1 (drop)] // bb1: { -// | Regular-Live variables here: [] -// | Drop-Live variables here: [_1] +// | Live variables at bb1[0]: [_1 (drop)] // StorageLive(_2); -// | Regular-Live variables here: [] -// | Drop-Live variables here: [_1] +// | Live variables at bb1[1]: [_1 (drop)] // _2 = const can_panic() -> [return: bb2, unwind: bb4]; // } // END rustc.node12.nll.0.mir diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs index 957a57428e3..96fd29dfe2f 100644 --- a/src/test/mir-opt/nll/liveness-drop-intra-block.rs +++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs @@ -25,23 +25,17 @@ fn main() { // END RUST SOURCE // START rustc.node12.nll.0.mir -// | Variables regular-live on entry to the block bb1: [] -// | Variables drop-live on entry to the block bb1: [] +// | Live variables on entry to bb1: [] // bb1: { -// | Regular-Live variables here: [] -// | Drop-Live variables here: [] +// | Live variables at bb1[0]: [] // _1 = const 55usize; -// | Regular-Live variables here: [_1] -// | Drop-Live variables here: [] +// | Live variables at bb1[1]: [_1] // StorageLive(_3); -// | Regular-Live variables here: [_1] -// | Drop-Live variables here: [] +// | Live variables at bb1[2]: [_1] // StorageLive(_4); -// | Regular-Live variables here: [_1] -// | Drop-Live variables here: [] +// | Live variables at bb1[3]: [_1] // _4 = _1; -// | Regular-Live variables here: [_4] -// | Drop-Live variables here: [] +// | Live variables at bb1[4]: [_4] // _3 = const use_x(_4) -> bb2; // } // END rustc.node12.nll.0.mir diff --git a/src/test/mir-opt/nll/liveness-interblock.rs b/src/test/mir-opt/nll/liveness-interblock.rs index f5a2a25a9b0..c557763c004 100644 --- a/src/test/mir-opt/nll/liveness-interblock.rs +++ b/src/test/mir-opt/nll/liveness-interblock.rs @@ -29,26 +29,20 @@ fn main() { // END RUST SOURCE // START rustc.node18.nll.0.mir -// | Variables regular-live on entry to the block bb2: [_1] -// | Variables drop-live on entry to the block bb2: [] +// | Live variables on entry to bb2: [_1] // bb2: { -// | Regular-Live variables here: [_1] -// | Drop-Live variables here: [] +// | Live variables at bb2[0]: [_1] // StorageLive(_4); -// | Regular-Live variables here: [_1] -// | Drop-Live variables here: [] +// | Live variables at bb2[1]: [_1] // _4 = _1; -// | Regular-Live variables here: [_4] -// | Drop-Live variables here: [] +// | Live variables at bb2[2]: [_4] // _3 = const make_live(_4) -> bb4; // } // END rustc.node18.nll.0.mir // START rustc.node18.nll.0.mir -// | Variables regular-live on entry to the block bb3: [] -// | Variables drop-live on entry to the block bb3: [] +// | Live variables on entry to bb3: [] // bb3: { -// | Regular-Live variables here: [] -// | Drop-Live variables here: [] +// | Live variables at bb3[0]: [] // _5 = const make_dead() -> bb5; // } // END rustc.node18.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index 3ab83a8eec3..fa4eb9627db 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -38,24 +38,19 @@ fn main() { // END rustc.node12.nll.0.mir // START rustc.node12.nll.0.mir // bb1: { -// | Regular-Live variables here: [_1, _3] -// | Drop-Live variables here: [] +// | Live variables at bb1[0]: [_1, _3] // _2 = &'_#0r _1[_3]; -// | Regular-Live variables here: [_2] -// | Drop-Live variables here: [] +// | Live variables at bb1[1]: [_2] // switchInt(const true) -> [0u8: bb3, otherwise: bb2]; // } // END rustc.node12.nll.0.mir // START rustc.node12.nll.0.mir // bb2: { -// | Regular-Live variables here: [_2] -// | Drop-Live variables here: [] +// | Live variables at bb2[0]: [_2] // StorageLive(_7); -// | Regular-Live variables here: [_2] -// | Drop-Live variables here: [] +// | Live variables at bb2[1]: [_2] // _7 = (*_2); -// | Regular-Live variables here: [_7] -// | Drop-Live variables here: [] +// | Live variables at bb2[2]: [_7] // _6 = const use_x(_7) -> bb4; // } // END rustc.node12.nll.0.mir From 5b2adcce115ee0e3fe4e53b57a2b9f168c26b288 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 25 Oct 2017 12:09:01 -0400 Subject: [PATCH 16/33] factor out NLL invocation interface --- src/librustc_mir/transform/nll/mod.rs | 124 +++++++++++------- .../transform/nll/region_infer.rs | 75 ++++++----- 2 files changed, 123 insertions(+), 76 deletions(-) diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 1498a6b23c0..8a62533ba33 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -42,44 +42,70 @@ impl MirPass for NLL { return; } - tcx.infer_ctxt().enter(|ref infcx| { - // Clone mir so we can mutate it without disturbing the rest of the compiler - let mir = &mut input_mir.clone(); - - // Replace all regions with fresh inference variables. - let num_region_variables = renumber::renumber_mir(infcx, mir); - - // Compute what is live where. - 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. - let regioncx = &mut RegionInferenceContext::new(num_region_variables); - constraint_generation::generate_constraints(infcx, regioncx, mir, source, liveness); - regioncx.solve(infcx, mir); - - // Dump MIR results into a file, if that is enabled. - dump_mir_results(infcx, liveness, source, regioncx, mir); - }) + tcx.infer_ctxt() + .enter(|ref infcx| drop(compute_regions(infcx, source, input_mir))); } } +pub struct RegionComputation<'tcx> { + /// A rewritten version of the input MIR where all the regions are + /// rewritten to refer to inference variables. + pub mir: Mir<'tcx>, + + /// The definitions (along with their final values) for all regions. + pub regioncx: RegionInferenceContext, +} + +/// Computes the (non-lexical) regions from the input MIR. +/// +/// This may result in errors being reported. +pub fn compute_regions<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + source: MirSource, + input_mir: &Mir<'tcx>, +) -> RegionComputation<'tcx> { + // Clone mir so we can mutate it without disturbing the rest of the compiler + let mut mir = input_mir.clone(); + + // Replace all regions with fresh inference variables. + let num_region_variables = renumber::renumber_mir(infcx, &mut mir); + + // Compute what is live where. + 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. + let mut regioncx = RegionInferenceContext::new(num_region_variables); + constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness); + let errors = regioncx.solve(infcx, &mir); + + assert!(errors.is_empty(), "FIXME: report region inference failures"); + + let computation = RegionComputation { mir, regioncx }; + + // Dump MIR results into a file, if that is enabled. This let us + // write unit-tests. + dump_mir_results(infcx, liveness, source, &computation); + + computation +} + struct LivenessResults { regular: LivenessResult, drop: LivenessResult, @@ -89,13 +115,17 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, liveness: &LivenessResults, source: MirSource, - regioncx: &RegionInferenceContext, - mir: &Mir<'tcx>, + computation: &RegionComputation<'tcx>, ) { if !mir_util::dump_enabled(infcx.tcx, "nll", source) { return; } + let RegionComputation { + ref mir, + ref regioncx, + } = *computation; + let regular_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks() .indices() .flat_map(|bb| { @@ -126,7 +156,12 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( match pass_where { // Before the CFG, dump out the values for each region variable. PassWhere::BeforeCFG => for region in regioncx.regions() { - writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?; + writeln!( + out, + "| {:?}: {:?}", + region, + regioncx.region_value(region) + )?; }, // Before each basic block, dump out the values @@ -141,12 +176,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( ®ular_liveness_per_location[&location], &drop_liveness_per_location[&location], ); - writeln!( - out, - " | Live variables at {:?}: {}", - location, - s - )?; + writeln!(out, " | Live variables at {:?}: {}", location, s)?; } PassWhere::AfterCFG => {} @@ -217,7 +247,11 @@ fn live_variable_set(regular: &LocalSet, drops: &LocalSet) -> String { string.push_str(", "); } - let len = if string.is_empty() { 0 } else { string.len() - 2 }; + let len = if string.is_empty() { + 0 + } else { + string.len() - 2 + }; format!("[{}]", &string[..len]) } diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index 75abd4d3ff5..c23d73e784a 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -12,13 +12,22 @@ use super::{Region, RegionIndex}; use std::mem; use rustc::infer::InferCtxt; use rustc::mir::{Location, Mir}; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::FxHashSet; pub struct RegionInferenceContext { - definitions: IndexVec, - constraints: IndexVec, - errors: IndexVec, + /// Contains the definition for every region variable. Region + /// variables are identified by their index (`RegionIndex`). The + /// definition contains information about where the region came + /// from as well as its final inferred value. + definitions: IndexVec, + + /// The constraints we have accumulated and used during solving. + constraints: Vec, + + /// List of errors we have accumulated as we add constraints. + /// After solving is done, this is replaced with an empty vector. + errors: Vec, } pub struct InferenceError { @@ -26,10 +35,8 @@ pub struct InferenceError { pub name: (), // FIXME(nashenas88) RegionName } -newtype_index!(InferenceErrorIndex); - #[derive(Default)] -struct VarDefinition { +struct RegionDefinition { name: (), // FIXME(nashenas88) RegionName value: Region, capped: bool, @@ -42,26 +49,43 @@ pub struct Constraint { point: Location, } -newtype_index!(ConstraintIndex); - impl RegionInferenceContext { pub fn new(num_region_variables: usize) -> Self { Self { definitions: (0..num_region_variables) - .map(|_| VarDefinition::default()) + .map(|_| RegionDefinition::default()) .collect(), - constraints: IndexVec::new(), - errors: IndexVec::new(), + constraints: Vec::new(), + errors: Vec::new(), } } + + /// Returns an iterator over all the region indices. + pub fn regions(&self) -> impl Iterator { + self.definitions.indices() + } + + /// Returns the inferred value for the region `r`. + /// + /// Until `solve()` executes, this value is not particularly meaningful. + pub fn region_value(&self, r: RegionIndex) -> &Region { + &self.definitions[r].value + } + + /// Flags a region as being "capped" -- this means that if its + /// value is required to grow as a result of some constraint + /// (e.g., `add_live_point` or `add_outlives`), that indicates an + /// error. This is used for the regions representing named + /// lifetime parameters on a function: they get initialized to + /// their complete value, and then "capped" so that they can no + /// longer grow. #[allow(dead_code)] - pub fn cap_var(&mut self, v: RegionIndex) { + pub(super) fn cap_var(&mut self, v: RegionIndex) { self.definitions[v].capped = true; } - #[allow(dead_code)] - pub fn add_live_point(&mut self, v: RegionIndex, point: Location) { + pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) { debug!("add_live_point({:?}, {:?})", v, point); let definition = &mut self.definitions[v]; if definition.value.add_point(point) { @@ -74,28 +98,17 @@ impl RegionInferenceContext { } } - #[allow(dead_code)] - pub fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) { + pub(super) fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) { debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); self.constraints.push(Constraint { sup, sub, point }); } - /// Returns an iterator over all the region indices. - pub fn regions(&self) -> impl Iterator { - self.definitions.indices() - } - - /// Returns the current value for the region `v`. This is only - /// really meaningful after `solve` has executed. - pub fn region_value(&self, v: RegionIndex) -> &Region { - &self.definitions[v].value - } - - pub fn solve<'a, 'gcx, 'tcx>( + /// Perform region inference. + pub(super) fn solve<'a, 'gcx, 'tcx>( &mut self, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - ) -> IndexVec + ) -> Vec where 'gcx: 'tcx + 'a, 'tcx: 'a, @@ -138,7 +151,7 @@ impl RegionInferenceContext { debug!("\n"); } - mem::replace(&mut self.errors, IndexVec::new()) + mem::replace(&mut self.errors, Vec::new()) } } From f700728a3bcb1f82b5457c4ac3051c672460ed7e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 25 Oct 2017 14:17:17 -0400 Subject: [PATCH 17/33] make end-point optional in the borrow check --- src/librustc_borrowck/borrowck/check_loans.rs | 3 +- src/librustc_mir/borrow_check.rs | 10 ++--- src/librustc_mir/dataflow/impls/borrows.rs | 7 +++- src/librustc_mir/util/borrowck_errors.rs | 38 ++++++++++++------- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index c1d0d849dfb..908737669c5 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -484,7 +484,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { // 3. Where does old loan expire. let previous_end_span = - old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree).end_point(); + Some(old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree) + .end_point()); let mut err = match (new_loan.kind, old_loan.kind) { (ty::MutBorrow, ty::MutBorrow) => diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index ee2ef00be57..38e77a96df9 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -425,8 +425,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> context, lvalue_span, borrow), ReadKind::Borrow(bk) => { let end_issued_loan_span = - flow_state.borrows.base_results.operator().region_span( - &borrow.region).end_point(); + flow_state.borrows.base_results.operator().opt_region_end_span( + &borrow.region); this.report_conflicting_borrow( context, common_prefix, lvalue_span, bk, &borrow, end_issued_loan_span) @@ -438,8 +438,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> match kind { WriteKind::MutableBorrow(bk) => { let end_issued_loan_span = - flow_state.borrows.base_results.operator().region_span( - &borrow.region).end_point(); + flow_state.borrows.base_results.operator().opt_region_end_span( + &borrow.region); this.report_conflicting_borrow( context, common_prefix, lvalue_span, bk, &borrow, end_issued_loan_span) @@ -1101,7 +1101,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> (lvalue, span): (&Lvalue, Span), gen_borrow_kind: BorrowKind, issued_borrow: &BorrowData, - end_issued_loan_span: Span) { + end_issued_loan_span: Option) { use self::prefixes::IsPrefixOf; assert!(common_prefix.is_prefix_of(lvalue)); diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 9321121fe15..396bfac8eb2 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -116,10 +116,13 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { &self.borrows[idx].location } - pub fn region_span(&self, region: &Region) -> Span { + /// Returns the span for the "end point" given region. This will + /// return `None` if NLL is enabled, since that concept has no + /// meaning there. Otherwise, it should return some. + pub fn opt_region_end_span(&self, region: &Region) -> Option { let opt_span = self.region_span_map.get(region); assert!(opt_span.is_some(), "end region not found for {:?}", region); - *opt_span.unwrap() + opt_span.map(|s| s.end_point()) } } diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index 5451da2148e..5cc960face9 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -93,7 +93,7 @@ pub trait BorrowckErrors { opt_via: &str, old_loan_span: Span, old_opt_via: &str, - old_load_end_span:Span, + old_load_end_span: Option, o: Origin) -> DiagnosticBuilder { @@ -106,13 +106,17 @@ pub trait BorrowckErrors { err.span_label(new_loan_span, format!("mutable borrow starts here in previous \ iteration of loop{}", opt_via)); - err.span_label(old_load_end_span, "mutable borrow ends here"); + if let Some(old_load_end_span) = old_load_end_span { + err.span_label(old_load_end_span, "mutable borrow ends here"); + } } else { err.span_label(old_loan_span, format!("first mutable borrow occurs here{}", old_opt_via)); err.span_label(new_loan_span, format!("second mutable borrow occurs here{}", opt_via)); - err.span_label(old_load_end_span, "first borrow ends here"); + if let Some(old_load_end_span) = old_load_end_span { + err.span_label(old_load_end_span, "first borrow ends here"); + } } err } @@ -121,7 +125,7 @@ pub trait BorrowckErrors { new_loan_span: Span, desc: &str, old_loan_span: Span, - old_load_end_span: Span, + old_load_end_span: Option, o: Origin) -> DiagnosticBuilder { @@ -134,9 +138,11 @@ pub trait BorrowckErrors { err.span_label( new_loan_span, "second closure is constructed here"); - err.span_label( - old_load_end_span, - "borrow from first closure ends here"); + if let Some(old_load_end_span) = old_load_end_span { + err.span_label( + old_load_end_span, + "borrow from first closure ends here"); + } err } @@ -147,7 +153,7 @@ pub trait BorrowckErrors { old_loan_span: Span, noun_old: &str, old_opt_via: &str, - previous_end_span: Span, + previous_end_span: Option, o: Origin) -> DiagnosticBuilder { @@ -158,7 +164,9 @@ pub trait BorrowckErrors { format!("closure construction occurs here{}", opt_via)); err.span_label(old_loan_span, format!("borrow occurs here{}", old_opt_via)); - err.span_label(previous_end_span, "borrow ends here"); + if let Some(previous_end_span) = previous_end_span { + err.span_label(previous_end_span, "borrow ends here"); + } err } @@ -169,7 +177,7 @@ pub trait BorrowckErrors { kind_new: &str, old_loan_span: Span, old_opt_via: &str, - previous_end_span: Span, + previous_end_span: Option, o: Origin) -> DiagnosticBuilder { @@ -181,7 +189,9 @@ pub trait BorrowckErrors { format!("borrow occurs here{}", opt_via)); err.span_label(old_loan_span, format!("closure construction occurs here{}", old_opt_via)); - err.span_label(previous_end_span, "borrow from closure ends here"); + if let Some(previous_end_span) = previous_end_span { + err.span_label(previous_end_span, "borrow from closure ends here"); + } err } @@ -194,7 +204,7 @@ pub trait BorrowckErrors { noun_old: &str, kind_old: &str, msg_old: &str, - old_load_end_span: Span, + old_load_end_span: Option, o: Origin) -> DiagnosticBuilder { @@ -203,7 +213,9 @@ pub trait BorrowckErrors { desc_new, msg_new, kind_new, noun_old, kind_old, msg_old, OGN=o); err.span_label(span, format!("{} borrow occurs here{}", kind_new, msg_new)); err.span_label(old_span, format!("{} borrow occurs here{}", kind_old, msg_old)); - err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old)); + if let Some(old_load_end_span) = old_load_end_span { + err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old)); + } err } From b8615f3bea67f12db7a84687923d62d5aabc22c1 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 25 Oct 2017 15:10:48 -0400 Subject: [PATCH 18/33] add reborrow constraints --- .../transform/nll/constraint_generation.rs | 35 +++++++++++++++++ src/test/mir-opt/nll/reborrow-basic.rs | 39 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/test/mir-opt/nll/reborrow-basic.rs diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs index 1acbd72a47d..1fc7dbd5bd0 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -8,9 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use rustc::hir; use rustc::mir::{BasicBlock, BorrowKind, Location, Lvalue, Mir, Rvalue, Statement, StatementKind}; use rustc::mir::transform::MirSource; use rustc::mir::visit::Visitor; +use rustc::mir::Lvalue::Projection; +use rustc::mir::{LvalueProjection, ProjectionElem}; use rustc::infer::InferCtxt; use rustc::traits::{self, ObligationCause}; use rustc::ty::{self, Ty}; @@ -198,6 +201,37 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { destination_region.to_region_index(), location.successor_within_block()); } + + fn add_reborrow_constraint( + &mut self, + location: Location, + borrow_region: ty::Region<'tcx>, + borrowed_lv: &Lvalue<'tcx>, + ) { + if let Projection(ref proj) = *borrowed_lv { + let LvalueProjection { ref base, ref elem } = **proj; + + if let ProjectionElem::Deref = *elem { + let tcx = self.infcx.tcx; + let base_ty = base.ty(self.mir, tcx).to_ty(tcx); + let base_sty = &base_ty.sty; + + if let ty::TyRef(base_region, ty::TypeAndMut{ ty: _, mutbl }) = *base_sty { + match mutbl { + hir::Mutability::MutImmutable => { }, + + hir::Mutability::MutMutable => { + self.add_reborrow_constraint(location, borrow_region, base); + }, + } + + self.regioncx.add_outlives(base_region.to_region_index(), + borrow_region.to_region_index(), + location.successor_within_block()); + } + } + } + } } impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> { @@ -214,6 +248,7 @@ impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> { if let StatementKind::Assign(ref destination_lv, ref rv) = statement.kind { if let Rvalue::Ref(region, bk, ref borrowed_lv) = *rv { self.add_borrow_constraint(location, destination_lv, region, bk, borrowed_lv); + self.add_reborrow_constraint(location, region, borrowed_lv); } } diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs new file mode 100644 index 00000000000..1983478a4e1 --- /dev/null +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -0,0 +1,39 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Basic test for reborrow constraints: the region (`R5`) that appears +// in the type of `r_a` must outlive the region (`R7`) that appears in +// the type of `r_b` + +// compile-flags:-Znll -Zverbose +// ^^^^^^^^^ force compiler to dump more region information + +#![allow(warnings)] + +fn use_x(_: &mut i32) -> bool { true } + +fn main() { + let mut foo: i32 = 22; + let r_a: &mut i32 = &mut foo; + let r_b: &mut i32 = &mut *r_a; + use_x(r_b); +} + +// END RUST SOURCE +// START rustc.node13.nll.0.mir +// | R5: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} +// ... +// | R7: {bb0[11], bb0[12], bb0[13], bb0[14]} +// END rustc.node13.nll.0.mir +// START rustc.node13.nll.0.mir +// let _2: &'_#5r mut i32; +// ... +// let _4: &'_#7r mut i32; +// END rustc.node13.nll.0.mir From dde61f38557335870a2d53bfa4099466fb71bd60 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 25 Oct 2017 18:04:57 -0400 Subject: [PATCH 19/33] add basic region subtyping inference --- .../transform/nll/constraint_generation.rs | 12 +++ src/librustc_mir/transform/nll/mod.rs | 1 + src/librustc_mir/transform/nll/subtype.rs | 99 +++++++++++++++++++ .../mir-opt/nll/region-subtyping-basic.rs | 49 +++++++++ 4 files changed, 161 insertions(+) create mode 100644 src/librustc_mir/transform/nll/subtype.rs create mode 100644 src/test/mir-opt/nll/region-subtyping-basic.rs diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs index 1fc7dbd5bd0..a7570c610d8 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -22,6 +22,7 @@ use rustc::util::common::ErrorReported; use rustc_data_structures::fx::FxHashSet; use syntax::codemap::DUMMY_SP; +use super::subtype; use super::LivenessResults; use super::ToRegionIndex; use super::region_infer::RegionInferenceContext; @@ -239,6 +240,9 @@ impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> { block: BasicBlock, statement: &Statement<'tcx>, location: Location) { + + debug!("visit_statement(statement={:?}, location={:?})", statement, location); + // Look for a statement like: // // D = & L @@ -250,6 +254,14 @@ impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> { self.add_borrow_constraint(location, destination_lv, region, bk, borrowed_lv); self.add_reborrow_constraint(location, region, borrowed_lv); } + + let tcx = self.infcx.tcx; + let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx); + let rv_ty = rv.ty(self.mir, tcx); + + for (a, b) in subtype::outlives_pairs(tcx, rv_ty, destination_ty) { + self.regioncx.add_outlives(a, b, location.successor_within_block()); + } } self.super_statement(block, statement, location); diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 8a62533ba33..3bd4f65d0dd 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -22,6 +22,7 @@ use util as mir_util; use self::mir_util::PassWhere; mod constraint_generation; +mod subtype; mod region_infer; use self::region_infer::RegionInferenceContext; diff --git a/src/librustc_mir/transform/nll/subtype.rs b/src/librustc_mir/transform/nll/subtype.rs new file mode 100644 index 00000000000..953fc0eb733 --- /dev/null +++ b/src/librustc_mir/transform/nll/subtype.rs @@ -0,0 +1,99 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::RegionIndex; +use transform::nll::ToRegionIndex; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation}; + +pub fn outlives_pairs<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + a: Ty<'tcx>, + b: Ty<'tcx>) + -> Vec<(RegionIndex, RegionIndex)> +{ + let mut subtype = Subtype::new(tcx); + match subtype.relate(&a, &b) { + Ok(_) => subtype.outlives_pairs, + + Err(_) => bug!("Fail to relate a = {:?} and b = {:?}", a, b) + } +} + +struct Subtype<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, + outlives_pairs: Vec<(RegionIndex, RegionIndex)>, + ambient_variance: ty::Variance, +} + +impl<'a, 'gcx, 'tcx> Subtype<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Subtype<'a, 'gcx, 'tcx> { + Subtype { + tcx, + outlives_pairs: vec![], + ambient_variance: ty::Covariant, + } + } +} + +impl<'a, 'gcx, 'tcx> TypeRelation<'a, 'gcx, 'tcx> for Subtype<'a, 'gcx, 'tcx> { + fn tag(&self) -> &'static str { "Subtype" } + fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.tcx } + fn a_is_expected(&self) -> bool { true } // irrelevant + + fn relate_with_variance>(&mut self, + variance: ty::Variance, + a: &T, + b: &T) + -> RelateResult<'tcx, T> + { + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + + let result = self.relate(a, b); + self.ambient_variance = old_ambient_variance; + result + } + + fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + relate::super_relate_tys(self, t, t2) + } + + fn regions(&mut self, r_a: ty::Region<'tcx>, r_b: ty::Region<'tcx>) + -> RelateResult<'tcx, ty::Region<'tcx>> { + let a = r_a.to_region_index(); + let b = r_b.to_region_index(); + + match self.ambient_variance { + ty::Covariant => { + self.outlives_pairs.push((b, a)); + }, + + ty::Invariant => { + self.outlives_pairs.push((a, b)); + self.outlives_pairs.push((b, a)); + }, + + ty::Contravariant => { + self.outlives_pairs.push((a, b)); + }, + + ty::Bivariant => {}, + } + + Ok(r_a) + } + + fn binders(&mut self, _a: &ty::Binder, _b: &ty::Binder) + -> RelateResult<'tcx, ty::Binder> + where T: Relate<'tcx> + { + unimplemented!(); + } +} diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs new file mode 100644 index 00000000000..bc97858e03d --- /dev/null +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -0,0 +1,49 @@ +// 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 or the MIT license +// , 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)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let p = &v[0]; + let q = p; + if true { + use_x(*q); + } else { + use_x(22); + } +} + +// END RUST SOURCE +// START rustc.node12.nll.0.mir +// | R0: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} +// | R1: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} +// | R2: {bb1[5], bb1[6], bb2[0], bb2[1]} +// END rustc.node12.nll.0.mir +// START rustc.node12.nll.0.mir +// let _2: &'_#1r usize; +// ... +// let _6: &'_#2r usize; +// ... +// _2 = &'_#0r _1[_3]; +// ... +// _7 = _2; +// ... +// _6 = _7; +// END rustc.node12.nll.0.mir From 68c4844a1bc35f25d7398e87069a3848d0633798 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 26 Oct 2017 09:48:20 -0400 Subject: [PATCH 20/33] make nll separately invokable --- src/librustc_mir/transform/nll/mod.rs | 42 +++++++++------------------ 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 3bd4f65d0dd..7df217c62e3 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -24,7 +24,7 @@ use self::mir_util::PassWhere; mod constraint_generation; mod subtype; -mod region_infer; +pub(crate) mod region_infer; use self::region_infer::RegionInferenceContext; mod renumber; @@ -43,33 +43,23 @@ impl MirPass for NLL { return; } - tcx.infer_ctxt() - .enter(|ref infcx| drop(compute_regions(infcx, source, input_mir))); + tcx.infer_ctxt().enter(|ref infcx| { + let mut mir = input_mir.clone(); + let _ = compute_regions(infcx, source, &mut mir); + }); } } -pub struct RegionComputation<'tcx> { - /// A rewritten version of the input MIR where all the regions are - /// rewritten to refer to inference variables. - pub mir: Mir<'tcx>, - - /// The definitions (along with their final values) for all regions. - pub regioncx: RegionInferenceContext, -} - /// Computes the (non-lexical) regions from the input MIR. /// /// This may result in errors being reported. pub fn compute_regions<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, source: MirSource, - input_mir: &Mir<'tcx>, -) -> RegionComputation<'tcx> { - // Clone mir so we can mutate it without disturbing the rest of the compiler - let mut mir = input_mir.clone(); - + mir: &mut Mir<'tcx>, +) -> RegionInferenceContext { // Replace all regions with fresh inference variables. - let num_region_variables = renumber::renumber_mir(infcx, &mut mir); + let num_region_variables = renumber::renumber_mir(infcx, mir); // Compute what is live where. let liveness = &LivenessResults { @@ -98,13 +88,11 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( assert!(errors.is_empty(), "FIXME: report region inference failures"); - let computation = RegionComputation { mir, regioncx }; - // Dump MIR results into a file, if that is enabled. This let us // write unit-tests. - dump_mir_results(infcx, liveness, source, &computation); + dump_mir_results(infcx, liveness, source, &mir, ®ioncx); - computation + regioncx } struct LivenessResults { @@ -116,17 +104,13 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, liveness: &LivenessResults, source: MirSource, - computation: &RegionComputation<'tcx>, + mir: &Mir<'tcx>, + regioncx: &RegionInferenceContext, ) { if !mir_util::dump_enabled(infcx.tcx, "nll", source) { return; } - let RegionComputation { - ref mir, - ref regioncx, - } = *computation; - let regular_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks() .indices() .flat_map(|bb| { @@ -216,7 +200,7 @@ newtype_index!(RegionIndex { /// assert that the region is a `ReVar` and convert the internal index /// into a `RegionIndex`. This is reasonable because in our MIR we /// replace all free regions with inference variables. -trait ToRegionIndex { +pub trait ToRegionIndex { fn to_region_index(&self) -> RegionIndex; } From 5aa1cbf18fb2844315c52aef9e31b39410fe00b1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 26 Oct 2017 09:48:33 -0400 Subject: [PATCH 21/33] make `MirSource::from_local_def_id` invokable with 'gcx and 'tcx --- src/librustc/mir/transform.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index 92e62b14693..6c90a5f38d0 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -40,7 +40,7 @@ pub enum MirSource { } impl<'a, 'gcx, 'tcx> MirSource { - pub fn from_local_def_id(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> MirSource { + pub fn from_local_def_id(tcx: TyCtxt<'a, 'gcx, 'tcx>, def_id: DefId) -> MirSource { let id = tcx.hir.as_local_node_id(def_id).expect("mir source requires local def-id"); Self::from_node(tcx, id) } From 29cdd7306206120a52265d633a36193349c7dbd4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 26 Oct 2017 09:49:04 -0400 Subject: [PATCH 22/33] make `closure_self_ty` invokable with `'gcx` and `'tcx` --- src/librustc_mir/build/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index b2f0ff57b62..77496c7b8f2 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -240,10 +240,10 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, /////////////////////////////////////////////////////////////////////////// // BuildMir -- walks a crate, looking for fn items and methods to build MIR from -pub fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - closure_expr_id: ast::NodeId, - body_id: hir::BodyId) - -> Ty<'tcx> { +pub fn closure_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + closure_expr_id: ast::NodeId, + body_id: hir::BodyId) + -> Ty<'tcx> { let closure_expr_hir_id = tcx.hir.node_to_hir_id(closure_expr_id); let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_hir_id); From b2c248efea07ec8b48386845fad9e5b6f46dc644 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 26 Oct 2017 09:49:29 -0400 Subject: [PATCH 23/33] reorder 'gcx and 'tcx in `BorrowckErrors` impl --- src/librustc_mir/util/borrowck_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index 5cc960face9..a4a7699abda 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -441,7 +441,7 @@ pub trait BorrowckErrors { } } -impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> { +impl<'b, 'gcx, 'tcx> BorrowckErrors for TyCtxt<'b, 'gcx, 'tcx> { fn struct_span_err_with_code<'a, S: Into>(&'a self, sp: S, msg: &str, From 82b287a8c890100678563112e49f7b0533b09c5d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 30 Oct 2017 05:28:46 -0400 Subject: [PATCH 24/33] test "needs drop" on region-erased, lifted types This will be important in next commit, since the input types will be tagged not with `'gcx` but rather `'tcx`. Also, using the region-erased, lifted types enables better caching. --- src/librustc_mir/dataflow/drop_flag_effects.rs | 8 ++++++-- src/librustc_mir/dataflow/move_paths/builder.rs | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/dataflow/drop_flag_effects.rs index bd41bce67da..8f34278e145 100644 --- a/src/librustc_mir/dataflow/drop_flag_effects.rs +++ b/src/librustc_mir/dataflow/drop_flag_effects.rs @@ -151,7 +151,9 @@ pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>( let ty = lvalue.ty(mir, tcx).to_ty(tcx); debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, lvalue, ty); - if ty.needs_drop(tcx, ctxt.param_env) { + let gcx = tcx.global_tcx(); + let erased_ty = gcx.lift(&tcx.erase_regions(&ty)).unwrap(); + if erased_ty.needs_drop(gcx, ctxt.param_env) { each_child(child); } else { debug!("on_all_drop_children_bits - skipping") @@ -196,7 +198,9 @@ pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>( // don't move out of non-Copy things let lvalue = &move_data.move_paths[path].lvalue; let ty = lvalue.ty(mir, tcx).to_ty(tcx); - if !ty.moves_by_default(tcx, param_env, DUMMY_SP) { + let gcx = tcx.global_tcx(); + let erased_ty = gcx.lift(&tcx.erase_regions(&ty)).unwrap(); + if !erased_ty.moves_by_default(gcx, param_env, DUMMY_SP) { continue; } diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 0790d937ceb..147a620c40e 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -352,8 +352,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { debug!("gather_move({:?}, {:?})", self.loc, lval); let tcx = self.builder.tcx; + let gcx = tcx.global_tcx(); let lv_ty = lval.ty(self.builder.mir, tcx).to_ty(tcx); - if !lv_ty.moves_by_default(tcx, self.builder.param_env, DUMMY_SP) { + let erased_ty = gcx.lift(&tcx.erase_regions(&lv_ty)).unwrap(); + if !erased_ty.moves_by_default(gcx, self.builder.param_env, DUMMY_SP) { debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", self.loc, lval, lv_ty); return } From 81449174f3fdc3c932f5a5560c91c2930616f6e0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 30 Oct 2017 05:50:39 -0400 Subject: [PATCH 25/33] make the dataflow / mir-borrowck types carry a `'tcx` lifetime Also, factor out `do_mir_borrowck`, which is the code that actually performs the MIR borrowck from within the scope of an inference context. This change should be a pure refactoring. --- src/librustc_mir/borrow_check.rs | 214 +++++++++--------- .../dataflow/drop_flag_effects.rs | 40 ++-- src/librustc_mir/dataflow/impls/borrows.rs | 14 +- src/librustc_mir/dataflow/impls/mod.rs | 66 +++--- src/librustc_mir/dataflow/mod.rs | 24 +- .../dataflow/move_paths/builder.rs | 34 +-- src/librustc_mir/dataflow/move_paths/mod.rs | 6 +- src/librustc_mir/transform/elaborate_drops.rs | 10 +- 8 files changed, 209 insertions(+), 199 deletions(-) diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index 38e77a96df9..5ff7bcc3c16 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -46,93 +46,103 @@ pub fn provide(providers: &mut Providers) { } fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { - let mir = tcx.mir_validated(def_id); + let input_mir = tcx.mir_validated(def_id); let src = MirSource::from_local_def_id(tcx, def_id); debug!("run query mir_borrowck: {}", tcx.node_path_str(src.item_id())); - let mir: &Mir<'tcx> = &mir.borrow(); if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.debugging_opts.borrowck_mir { return; } - let id = src.item_id(); + tcx.infer_ctxt().enter(|infcx| { + let input_mir: &Mir = &input_mir.borrow(); + do_mir_borrowck(&infcx, input_mir, def_id, src); + }); + debug!("mir_borrowck done"); +} + +fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, + mir: &Mir<'gcx>, + def_id: DefId, + src: MirSource) +{ + let tcx = infcx.tcx; let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); - tcx.infer_ctxt().enter(|_infcx| { - let move_data = match MoveData::gather_moves(mir, tcx, param_env) { - Ok(move_data) => move_data, - Err((move_data, move_errors)) => { - for move_error in move_errors { - let (span, kind): (Span, IllegalMoveOriginKind) = match move_error { - MoveError::UnionMove { .. } => - unimplemented!("dont know how to report union move errors yet."), - MoveError::IllegalMove { cannot_move_out_of: o } => (o.span, o.kind), - }; - let origin = Origin::Mir; - let mut err = match kind { - IllegalMoveOriginKind::Static => - tcx.cannot_move_out_of(span, "static item", origin), - IllegalMoveOriginKind::BorrowedContent => - tcx.cannot_move_out_of(span, "borrowed_content", origin), - IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => - tcx.cannot_move_out_of_interior_of_drop(span, ty, origin), - IllegalMoveOriginKind::InteriorOfSlice { elem_ty: ty, is_index } => - tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin), - IllegalMoveOriginKind::InteriorOfArray { elem_ty: ty, is_index } => - tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin), - }; - err.emit(); - } - move_data + let id = src.item_id(); + + let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx, param_env) { + Ok(move_data) => move_data, + Err((move_data, move_errors)) => { + for move_error in move_errors { + let (span, kind): (Span, IllegalMoveOriginKind) = match move_error { + MoveError::UnionMove { .. } => + unimplemented!("dont know how to report union move errors yet."), + MoveError::IllegalMove { cannot_move_out_of: o } => (o.span, o.kind), + }; + let origin = Origin::Mir; + let mut err = match kind { + IllegalMoveOriginKind::Static => + tcx.cannot_move_out_of(span, "static item", origin), + IllegalMoveOriginKind::BorrowedContent => + tcx.cannot_move_out_of(span, "borrowed_content", origin), + IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => + tcx.cannot_move_out_of_interior_of_drop(span, ty, origin), + IllegalMoveOriginKind::InteriorOfSlice { elem_ty: ty, is_index } => + tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin), + IllegalMoveOriginKind::InteriorOfArray { elem_ty: ty, is_index } => + tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin), + }; + err.emit(); } - }; - let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; - let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); - let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - Borrows::new(tcx, mir), - |bd, i| bd.location(i)); - let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - MaybeInitializedLvals::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().move_paths[i]); - let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - MaybeUninitializedLvals::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().move_paths[i]); + move_data + } + }; - let mut mbcx = MirBorrowckCtxt { - tcx: tcx, - mir: mir, - node_id: id, - move_data: &mdpe.move_data, - param_env: param_env, - fake_infer_ctxt: &_infcx, - }; + let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; + let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); + let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, + Borrows::new(tcx, mir), + |bd, i| bd.location(i)); + let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, + MaybeInitializedLvals::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().move_paths[i]); + let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, + MaybeUninitializedLvals::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().move_paths[i]); - let mut state = InProgress::new(flow_borrows, - flow_inits, - flow_uninits); + let mut mbcx = MirBorrowckCtxt { + tcx: tcx, + mir: mir, + node_id: id, + move_data: &mdpe.move_data, + param_env: param_env, + fake_infer_ctxt: &infcx, + }; - mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer - }); + let mut state = InProgress::new(flow_borrows, + flow_inits, + flow_uninits); - debug!("mir_borrowck done"); + mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer } #[allow(dead_code)] pub struct MirBorrowckCtxt<'c, 'b, 'a: 'b+'c, 'gcx: 'a+'tcx, 'tcx: 'a> { - tcx: TyCtxt<'a, 'gcx, 'gcx>, - mir: &'b Mir<'gcx>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &'b Mir<'tcx>, node_id: ast::NodeId, - move_data: &'b MoveData<'gcx>, + move_data: &'b MoveData<'tcx>, param_env: ParamEnv<'tcx>, fake_infer_ctxt: &'c InferCtxt<'c, 'gcx, 'tcx>, } // (forced to be `pub` due to its use as an associated type below.) -pub struct InProgress<'b, 'tcx: 'b> { - borrows: FlowInProgress>, - inits: FlowInProgress>, - uninits: FlowInProgress>, +pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> { + borrows: FlowInProgress>, + inits: FlowInProgress>, + uninits: FlowInProgress>, } struct FlowInProgress where BD: BitDenotation { @@ -147,12 +157,12 @@ struct FlowInProgress where BD: BitDenotation { // 2. loans made in overlapping scopes do not conflict // 3. assignments do not affect things loaned out as immutable // 4. moves do not affect things loaned out in any way -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx> +impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx> for MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { - type FlowState = InProgress<'b, 'gcx>; + type FlowState = InProgress<'b, 'gcx, 'tcx>; - fn mir(&self) -> &'b Mir<'gcx> { self.mir } + fn mir(&self) -> &'b Mir<'tcx> { self.mir } fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) { flow_state.each_flow(|b| b.reset_to_entry_of(bb), @@ -193,7 +203,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx> fn visit_statement_entry(&mut self, location: Location, - stmt: &Statement<'gcx>, + stmt: &Statement<'tcx>, flow_state: &Self::FlowState) { let summary = flow_state.summary(); debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {}", location, stmt, summary); @@ -261,7 +271,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx> fn visit_terminator_entry(&mut self, location: Location, - term: &Terminator<'gcx>, + term: &Terminator<'tcx>, flow_state: &Self::FlowState) { let loc = location; let summary = flow_state.summary(); @@ -405,9 +415,9 @@ enum WriteKind { impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { fn access_lvalue(&mut self, context: Context, - lvalue_span: (&Lvalue<'gcx>, Span), + lvalue_span: (&Lvalue<'tcx>, Span), kind: (ShallowOrDeep, ReadOrWrite), - flow_state: &InProgress<'b, 'gcx>) { + flow_state: &InProgress<'b, 'gcx, 'tcx>) { // FIXME: also need to check permissions (e.g. reject mut // borrow of immutable ref, moves through non-`Box`-ref) let (sd, rw) = kind; @@ -460,10 +470,10 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> fn mutate_lvalue(&mut self, context: Context, - lvalue_span: (&Lvalue<'gcx>, Span), + lvalue_span: (&Lvalue<'tcx>, Span), kind: ShallowOrDeep, mode: MutateMode, - flow_state: &InProgress<'b, 'gcx>) { + flow_state: &InProgress<'b, 'gcx, 'tcx>) { // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd. match mode { MutateMode::WriteAndRead => { @@ -482,9 +492,9 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> fn consume_rvalue(&mut self, context: Context, - (rvalue, span): (&Rvalue<'gcx>, Span), + (rvalue, span): (&Rvalue<'tcx>, Span), _location: Location, - flow_state: &InProgress<'b, 'gcx>) { + flow_state: &InProgress<'b, 'gcx, 'tcx>) { match *rvalue { Rvalue::Ref(_/*rgn*/, bk, ref lvalue) => { let access_kind = match bk { @@ -540,8 +550,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> fn consume_operand(&mut self, context: Context, consume_via_drop: ConsumeKind, - (operand, span): (&Operand<'gcx>, Span), - flow_state: &InProgress<'b, 'gcx>) { + (operand, span): (&Operand<'tcx>, Span), + flow_state: &InProgress<'b, 'gcx, 'tcx>) { match *operand { Operand::Consume(ref lvalue) => { self.consume_lvalue(context, consume_via_drop, (lvalue, span), flow_state) @@ -553,8 +563,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> fn consume_lvalue(&mut self, context: Context, consume_via_drop: ConsumeKind, - lvalue_span: (&Lvalue<'gcx>, Span), - flow_state: &InProgress<'b, 'gcx>) { + lvalue_span: (&Lvalue<'tcx>, Span), + flow_state: &InProgress<'b, 'gcx, 'tcx>) { let lvalue = lvalue_span.0; let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); let moves_by_default = @@ -584,8 +594,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { fn check_if_reassignment_to_immutable_state(&mut self, context: Context, - (lvalue, span): (&Lvalue<'gcx>, Span), - flow_state: &InProgress<'b, 'gcx>) { + (lvalue, span): (&Lvalue<'tcx>, Span), + flow_state: &InProgress<'b, 'gcx, 'tcx>) { let move_data = self.move_data; // determine if this path has a non-mut owner (and thus needs checking). @@ -635,8 +645,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> fn check_if_path_is_moved(&mut self, context: Context, desired_action: &str, - lvalue_span: (&Lvalue<'gcx>, Span), - flow_state: &InProgress<'b, 'gcx>) { + lvalue_span: (&Lvalue<'tcx>, Span), + flow_state: &InProgress<'b, 'gcx, 'tcx>) { // FIXME: analogous code in check_loans first maps `lvalue` to // its base_path ... but is that what we want here? let lvalue = self.base_path(lvalue_span.0); @@ -725,7 +735,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> /// An Err result includes a tag indicated why the search failed. /// Currenly this can only occur if the lvalue is built off of a /// static variable, as we do not track those in the MoveData. - fn move_path_closest_to(&mut self, lvalue: &Lvalue<'gcx>) + fn move_path_closest_to(&mut self, lvalue: &Lvalue<'tcx>) -> Result { let mut last_prefix = lvalue; @@ -743,7 +753,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> } fn move_path_for_lvalue(&mut self, - lvalue: &Lvalue<'gcx>) + lvalue: &Lvalue<'tcx>) -> Option { // If returns None, then there is no move path corresponding @@ -758,8 +768,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> fn check_if_assigned_path_is_moved(&mut self, context: Context, - (lvalue, span): (&Lvalue<'gcx>, Span), - flow_state: &InProgress<'b, 'gcx>) { + (lvalue, span): (&Lvalue<'tcx>, Span), + flow_state: &InProgress<'b, 'gcx, 'tcx>) { // recur down lvalue; dispatch to check_if_path_is_moved when necessary let mut lvalue = lvalue; loop { @@ -827,10 +837,10 @@ enum NoMovePathFound { impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { fn each_borrow_involving_path(&mut self, _context: Context, - access_lvalue: (ShallowOrDeep, &Lvalue<'gcx>), - flow_state: &InProgress<'b, 'gcx>, + access_lvalue: (ShallowOrDeep, &Lvalue<'tcx>), + flow_state: &InProgress<'b, 'gcx, 'tcx>, mut op: F) - where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'gcx>, &Lvalue) -> Control + where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Lvalue) -> Control { let (access, lvalue) = access_lvalue; @@ -928,9 +938,9 @@ mod prefixes { } - pub(super) struct Prefixes<'c, 'tcx: 'c> { + pub(super) struct Prefixes<'c, 'gcx: 'tcx, 'tcx: 'c> { mir: &'c Mir<'tcx>, - tcx: TyCtxt<'c, 'tcx, 'tcx>, + tcx: TyCtxt<'c, 'gcx, 'tcx>, kind: PrefixSet, next: Option<&'c Lvalue<'tcx>>, } @@ -951,15 +961,15 @@ mod prefixes { /// (inclusive) from longest to smallest, potentially /// terminating the iteration early based on `kind`. pub(super) fn prefixes<'d>(&self, - lvalue: &'d Lvalue<'gcx>, + lvalue: &'d Lvalue<'tcx>, kind: PrefixSet) - -> Prefixes<'d, 'gcx> where 'b: 'd + -> Prefixes<'d, 'gcx, 'tcx> where 'b: 'd { Prefixes { next: Some(lvalue), kind, mir: self.mir, tcx: self.tcx } } } - impl<'c, 'tcx> Iterator for Prefixes<'c, 'tcx> { + impl<'c, 'gcx, 'tcx> Iterator for Prefixes<'c, 'gcx, 'tcx> { type Item = &'c Lvalue<'tcx>; fn next(&mut self) -> Option { let mut cursor = match self.next { @@ -1315,7 +1325,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> // moves out of a Box. They should be removed when/if we stop // treating Box specially (e.g. when/if DerefMove is added...) - fn base_path<'d>(&self, lvalue: &'d Lvalue<'gcx>) -> &'d Lvalue<'gcx> { + fn base_path<'d>(&self, lvalue: &'d Lvalue<'tcx>) -> &'d Lvalue<'tcx> { //! Returns the base of the leftmost (deepest) dereference of an //! Box in `lvalue`. If there is no dereference of an Box //! in `lvalue`, then it just returns `lvalue` itself. @@ -1364,10 +1374,10 @@ impl ContextKind { fn new(self, loc: Location) -> Context { Context { kind: self, loc: loc } } } -impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> { - pub(super) fn new(borrows: DataflowResults>, - inits: DataflowResults>, - uninits: DataflowResults>) +impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { + pub(super) fn new(borrows: DataflowResults>, + inits: DataflowResults>, + uninits: DataflowResults>) -> Self { InProgress { borrows: FlowInProgress::new(borrows), @@ -1380,9 +1390,9 @@ impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> { mut xform_borrows: XB, mut xform_inits: XI, mut xform_uninits: XU) where - XB: FnMut(&mut FlowInProgress>), - XI: FnMut(&mut FlowInProgress>), - XU: FnMut(&mut FlowInProgress>), + XB: FnMut(&mut FlowInProgress>), + XI: FnMut(&mut FlowInProgress>), + XU: FnMut(&mut FlowInProgress>), { xform_borrows(&mut self.borrows); xform_inits(&mut self.inits); @@ -1438,7 +1448,7 @@ impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> { } } -impl<'b, 'tcx> FlowInProgress> { +impl<'b, 'gcx, 'tcx> FlowInProgress> { fn has_any_child_of(&self, mpi: MovePathIndex) -> Option { let move_data = self.base_results.operator().move_data(); diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/dataflow/drop_flag_effects.rs index 8f34278e145..e35bd34c40b 100644 --- a/src/librustc_mir/dataflow/drop_flag_effects.rs +++ b/src/librustc_mir/dataflow/drop_flag_effects.rs @@ -58,9 +58,9 @@ pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>, /// is no need to maintain separate drop flags to track such state. /// /// FIXME: we have to do something for moving slice patterns. -fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - mir: &Mir<'tcx>, - lv: &mir::Lvalue<'tcx>) -> bool { +fn lvalue_contents_drop_state_cannot_differ<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + lv: &mir::Lvalue<'tcx>) -> bool { let ty = lv.ty(mir, tcx).to_ty(tcx); match ty.sty { ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => { @@ -79,8 +79,8 @@ fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx } } -pub(crate) fn on_lookup_result_bits<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn on_lookup_result_bits<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, move_data: &MoveData<'tcx>, lookup_result: LookupResult, @@ -97,16 +97,16 @@ pub(crate) fn on_lookup_result_bits<'a, 'tcx, F>( } } -pub(crate) fn on_all_children_bits<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn on_all_children_bits<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, move_data: &MoveData<'tcx>, move_path_index: MovePathIndex, mut each_child: F) where F: FnMut(MovePathIndex) { - fn is_terminal_path<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, + fn is_terminal_path<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, move_data: &MoveData<'tcx>, path: MovePathIndex) -> bool @@ -115,8 +115,8 @@ pub(crate) fn on_all_children_bits<'a, 'tcx, F>( tcx, mir, &move_data.move_paths[path].lvalue) } - fn on_all_children_bits<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, + fn on_all_children_bits<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, move_data: &MoveData<'tcx>, move_path_index: MovePathIndex, @@ -138,10 +138,10 @@ pub(crate) fn on_all_children_bits<'a, 'tcx, F>( on_all_children_bits(tcx, mir, move_data, move_path_index, &mut each_child); } -pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn on_all_drop_children_bits<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, - ctxt: &MoveDataParamEnv<'tcx>, + ctxt: &MoveDataParamEnv<'gcx, 'tcx>, path: MovePathIndex, mut each_child: F) where F: FnMut(MovePathIndex) @@ -161,10 +161,10 @@ pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>( }) } -pub(crate) fn drop_flag_effects_for_function_entry<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn drop_flag_effects_for_function_entry<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, - ctxt: &MoveDataParamEnv<'tcx>, + ctxt: &MoveDataParamEnv<'gcx, 'tcx>, mut callback: F) where F: FnMut(MovePathIndex, DropFlagState) { @@ -178,10 +178,10 @@ pub(crate) fn drop_flag_effects_for_function_entry<'a, 'tcx, F>( } } -pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn drop_flag_effects_for_location<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, - ctxt: &MoveDataParamEnv<'tcx>, + ctxt: &MoveDataParamEnv<'gcx, 'tcx>, loc: Location, mut callback: F) where F: FnMut(MovePathIndex, DropFlagState) diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 396bfac8eb2..8079f64bf5d 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -29,8 +29,8 @@ use std::fmt; // `Borrows` maps each dataflow bit to an `Rvalue::Ref`, which can be // uniquely identified in the MIR by the `Location` of the assigment // statement in which it appears on the right hand side. -pub struct Borrows<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, borrows: IndexVec>, location_map: FxHashMap, @@ -63,8 +63,8 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> { } } -impl<'a, 'tcx> Borrows<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { +impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { let mut visitor = GatherBorrows { idx_vec: IndexVec::new(), location_map: FxHashMap(), region_map: FxHashMap(), @@ -126,7 +126,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { } } -impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { type Idx = BorrowIndex; fn name() -> &'static str { "borrows" } fn bits_per_block(&self) -> usize { @@ -191,14 +191,14 @@ impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> { } } -impl<'a, 'tcx> BitwiseOperator for Borrows<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitwiseOperator for Borrows<'a, 'gcx, 'tcx> { #[inline] fn join(&self, pred1: usize, pred2: usize) -> usize { pred1 | pred2 // union effects of preds when computing borrows } } -impl<'a, 'tcx> DataflowOperator for Borrows<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> DataflowOperator for Borrows<'a, 'gcx, 'tcx> { #[inline] fn bottom_value() -> bool { false // bottom = no Rvalue::Refs are active by default diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index 19a595622b9..af99706be81 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -69,23 +69,23 @@ pub(super) mod borrows; /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeUninitializedLvals` yields the set of /// l-values that would require a dynamic drop-flag at that statement. -pub struct MaybeInitializedLvals<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub struct MaybeInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>, + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, } -impl<'a, 'tcx: 'a> MaybeInitializedLvals<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, +impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>) + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) -> Self { MaybeInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe } } } -impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'gcx, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } } @@ -124,23 +124,23 @@ impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'tcx> { /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeInitializedLvals` yields the set of /// l-values that would require a dynamic drop-flag at that statement. -pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub struct MaybeUninitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>, + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, } -impl<'a, 'tcx: 'a> MaybeUninitializedLvals<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, +impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>) + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) -> Self { MaybeUninitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe } } } -impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } } @@ -185,27 +185,27 @@ impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'tcx> { /// Similarly, at a given `drop` statement, the set-difference between /// this data and `MaybeInitializedLvals` yields the set of l-values /// that would require a dynamic drop-flag at that statement. -pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub struct DefinitelyInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>, + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, } -impl<'a, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, +impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>) + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) -> Self { DefinitelyInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe } } } -impl<'a, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } } -impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> { fn update_bits(sets: &mut BlockSets, path: MovePathIndex, state: DropFlagState) { @@ -216,7 +216,7 @@ impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> { } } -impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> { fn update_bits(sets: &mut BlockSets, path: MovePathIndex, state: DropFlagState) { @@ -227,7 +227,7 @@ impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> { } } -impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { fn update_bits(sets: &mut BlockSets, path: MovePathIndex, state: DropFlagState) { @@ -238,7 +238,7 @@ impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { } } -impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'gcx, 'tcx> { type Idx = MovePathIndex; fn name() -> &'static str { "maybe_init" } fn bits_per_block(&self) -> usize { @@ -290,7 +290,7 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { } } -impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { type Idx = MovePathIndex; fn name() -> &'static str { "maybe_uninit" } fn bits_per_block(&self) -> usize { @@ -345,7 +345,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { } } -impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { type Idx = MovePathIndex; fn name() -> &'static str { "definite_init" } fn bits_per_block(&self) -> usize { @@ -399,21 +399,21 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { } } -impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn join(&self, pred1: usize, pred2: usize) -> usize { pred1 | pred2 // "maybe" means we union effects of both preds } } -impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn join(&self, pred1: usize, pred2: usize) -> usize { pred1 | pred2 // "maybe" means we union effects of both preds } } -impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn join(&self, pred1: usize, pred2: usize) -> usize { pred1 & pred2 // "definitely" means we intersect effects of both preds @@ -430,21 +430,21 @@ impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> { // propagating, or you start at all-ones and then use Intersect as // your merge when propagating. -impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn bottom_value() -> bool { false // bottom = uninitialized } } -impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn bottom_value() -> bool { false // bottom = initialized (start_block_effect counters this at outset) } } -impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn bottom_value() -> bool { true // bottom = initialized (start_block_effect counters this at outset) diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index 9fa5691d647..d27a4e7e9d9 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -91,19 +91,19 @@ pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option return None; } -pub struct MoveDataParamEnv<'tcx> { +pub struct MoveDataParamEnv<'gcx, 'tcx> { pub(crate) move_data: MoveData<'tcx>, - pub(crate) param_env: ty::ParamEnv<'tcx>, + pub(crate) param_env: ty::ParamEnv<'gcx>, } -pub(crate) fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - mir: &Mir<'tcx>, - node_id: ast::NodeId, - attributes: &[ast::Attribute], - dead_unwinds: &IdxSet, - bd: BD, - p: P) - -> DataflowResults +pub(crate) fn do_dataflow<'a, 'gcx, 'tcx, BD, P>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + node_id: ast::NodeId, + attributes: &[ast::Attribute], + dead_unwinds: &IdxSet, + bd: BD, + p: P) + -> DataflowResults where BD: BitDenotation, P: Fn(&BD, BD::Idx) -> &fmt::Debug { @@ -612,9 +612,9 @@ pub trait BitDenotation: DataflowOperator { dest_lval: &mir::Lvalue); } -impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation +impl<'a, 'gcx, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation { - pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub fn new(_tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, dead_unwinds: &'a IdxSet, denotation: D) -> Self { diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 147a620c40e..8f473d035ee 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -25,18 +25,18 @@ use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, Move use super::{MoveError}; use super::IllegalMoveOriginKind::*; -struct MoveDataBuilder<'a, 'tcx: 'a> { +struct MoveDataBuilder<'a, 'gcx: 'tcx, 'tcx: 'a> { mir: &'a Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'gcx>, data: MoveData<'tcx>, errors: Vec>, } -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { fn new(mir: &'a Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) + tcx: TyCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'gcx>) -> Self { let mut move_paths = IndexVec::new(); let mut path_map = IndexVec::new(); @@ -86,7 +86,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } -impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { +impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { /// This creates a MovePath for a given lvalue, returning an `MovePathError` /// if that lvalue can't be moved from. /// @@ -175,7 +175,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { } } -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { fn finalize(self) -> Result, (MoveData<'tcx>, Vec>)> { debug!("{}", { debug!("moves for {:?}:", self.mir.span); @@ -197,11 +197,11 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } -pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) - -> Result, - (MoveData<'tcx>, Vec>)> { +pub(super) fn gather_moves<'a, 'gcx, 'tcx>(mir: &Mir<'tcx>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'gcx>) + -> Result, + (MoveData<'tcx>, Vec>)> { let mut builder = MoveDataBuilder::new(mir, tcx, param_env); for (bb, block) in mir.basic_blocks().iter_enumerated() { @@ -220,7 +220,7 @@ pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, builder.finalize() } -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) { debug!("gather_statement({:?}, {:?})", loc, stmt); (Gatherer { builder: self, loc }).gather_statement(stmt); @@ -232,12 +232,12 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } -struct Gatherer<'b, 'a: 'b, 'tcx: 'a> { - builder: &'b mut MoveDataBuilder<'a, 'tcx>, +struct Gatherer<'b, 'a: 'b, 'gcx: 'tcx, 'tcx: 'a> { + builder: &'b mut MoveDataBuilder<'a, 'gcx, 'tcx>, loc: Location, } -impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { +impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { fn gather_statement(&mut self, stmt: &Statement<'tcx>) { match stmt.kind { StatementKind::Assign(ref lval, ref rval) => { diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 9369156a223..5bfecd01aaa 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -256,10 +256,10 @@ impl<'tcx> MoveError<'tcx> { } } -impl<'a, 'tcx> MoveData<'tcx> { +impl<'a, 'gcx, 'tcx> MoveData<'tcx> { pub fn gather_moves(mir: &Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) + tcx: TyCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'gcx>) -> Result>)> { builder::gather_moves(mir, tcx, param_env) } diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index be1b794ecdf..94da1f31a96 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -83,7 +83,7 @@ fn find_dead_unwinds<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, id: ast::NodeId, - env: &MoveDataParamEnv<'tcx>) + env: &MoveDataParamEnv<'tcx, 'tcx>) -> IdxSetBuf { debug!("find_dead_unwinds({:?})", mir.span); @@ -146,7 +146,7 @@ impl InitializationData { fn apply_location<'a,'tcx>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, - env: &MoveDataParamEnv<'tcx>, + env: &MoveDataParamEnv<'tcx, 'tcx>, loc: Location) { drop_flag_effects_for_location(tcx, mir, env, loc, |path, df| { @@ -280,9 +280,9 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { struct ElaborateDropsCtxt<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, - env: &'a MoveDataParamEnv<'tcx>, - flow_inits: DataflowResults>, - flow_uninits: DataflowResults>, + env: &'a MoveDataParamEnv<'tcx, 'tcx>, + flow_inits: DataflowResults>, + flow_uninits: DataflowResults>, drop_flags: FxHashMap, patch: MirPatch<'tcx>, } From a94b01a0e244a1df75f90cd8f0bfee546b4b2690 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 30 Oct 2017 08:28:07 -0400 Subject: [PATCH 26/33] connect MIR borrowck with NLL --- src/librustc_mir/borrow_check.rs | 22 ++++++-- src/librustc_mir/dataflow/impls/borrows.rs | 40 ++++++++++++--- .../nll/loan_ends_mid_block_pair.rs | 50 +++++++++++++++++++ .../nll/loan_ends_mid_block_vec.rs | 49 ++++++++++++++++++ .../nll/region-ends-after-if-condition.rs | 32 ++++++++++++ src/test/compile-fail/nll/return_from_loop.rs | 49 ++++++++++++++++++ 6 files changed, 232 insertions(+), 10 deletions(-) create mode 100644 src/test/compile-fail/nll/loan_ends_mid_block_pair.rs create mode 100644 src/test/compile-fail/nll/loan_ends_mid_block_vec.rs create mode 100644 src/test/compile-fail/nll/region-ends-after-if-condition.rs create mode 100644 src/test/compile-fail/nll/return_from_loop.rs diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index 5ff7bcc3c16..8b5f1539ac0 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -17,7 +17,8 @@ use rustc::ty::maps::Providers; use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue, Local}; use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue}; use rustc::mir::{Statement, StatementKind, Terminator, TerminatorKind}; -use rustc::mir::transform::{MirSource}; +use rustc::mir::transform::MirSource; +use transform::nll; use rustc_data_structures::indexed_set::{self, IdxSetBuf}; use rustc_data_structures::indexed_vec::{Idx}; @@ -62,7 +63,7 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { } fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - mir: &Mir<'gcx>, + input_mir: &Mir<'gcx>, def_id: DefId, src: MirSource) { @@ -72,7 +73,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, let id = src.item_id(); - let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx, param_env) { + let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx, param_env) { Ok(move_data) => move_data, Err((move_data, move_errors)) => { for move_error in move_errors { @@ -100,10 +101,23 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, } }; + // Make our own copy of the MIR. This copy will be modified (in place) to + // contain non-lexical lifetimes. It will have a lifetime tied + // to the inference context. + let mut mir: Mir<'tcx> = input_mir.clone(); + let mir = &mut mir; + + // If we are in non-lexical mode, compute the non-lexical lifetimes. + let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll { + None + } else { + Some(nll::compute_regions(infcx, src, mir)) + }; + let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - Borrows::new(tcx, mir), + Borrows::new(tcx, mir, opt_regioncx.as_ref()), |bd, i| bd.location(i)); let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, MaybeInitializedLvals::new(tcx, mir, &mdpe), diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 8079f64bf5d..17aa8c05418 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -21,6 +21,8 @@ use rustc_data_structures::indexed_vec::{IndexVec}; use dataflow::{BitDenotation, BlockSets, DataflowOperator}; pub use dataflow::indexes::BorrowIndex; +use transform::nll::region_infer::RegionInferenceContext; +use transform::nll::ToRegionIndex; use syntax_pos::Span; @@ -36,6 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> { location_map: FxHashMap, region_map: FxHashMap, FxHashSet>, region_span_map: FxHashMap, + nonlexical_regioncx: Option<&'a RegionInferenceContext>, } // temporarily allow some dead fields: `kind` and `region` will be @@ -64,7 +67,10 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> { } impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, + nonlexical_regioncx: Option<&'a RegionInferenceContext>) + -> Self { let mut visitor = GatherBorrows { idx_vec: IndexVec::new(), location_map: FxHashMap(), region_map: FxHashMap(), @@ -75,7 +81,8 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { borrows: visitor.idx_vec, location_map: visitor.location_map, region_map: visitor.region_map, - region_span_map: visitor.region_span_map}; + region_span_map: visitor.region_span_map, + nonlexical_regioncx }; struct GatherBorrows<'tcx> { idx_vec: IndexVec>, @@ -121,9 +128,26 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { /// meaning there. Otherwise, it should return some. pub fn opt_region_end_span(&self, region: &Region) -> Option { let opt_span = self.region_span_map.get(region); - assert!(opt_span.is_some(), "end region not found for {:?}", region); + assert!(self.nonlexical_regioncx.is_some() || + opt_span.is_some(), "end region not found for {:?}", region); opt_span.map(|s| s.end_point()) } + + /// Add all borrows to the kill set, if those borrows are out of scope at `location`. + fn kill_loans_out_of_scope_at_location(&self, + sets: &mut BlockSets, + location: Location) { + if let Some(regioncx) = self.nonlexical_regioncx { + for (borrow_index, borrow_data) in self.borrows.iter_enumerated() { + let borrow_region = regioncx.region_value(borrow_data.region.to_region_index()); + if !borrow_region.may_contain(location) && location != borrow_data.location { + debug!("kill_loans_out_of_scope_at_location: kill{:?} \ + location={:?} borrow_data={:?}", borrow_index, location, borrow_data); + sets.kill(&borrow_index); + } + } + } + } } impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { @@ -149,6 +173,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { match stmt.kind { mir::StatementKind::EndRegion(region_scope) => { if let Some(borrow_indexes) = self.region_map.get(&ReScope(region_scope)) { + assert!(self.nonlexical_regioncx.is_none()); for idx in borrow_indexes { sets.kill(&idx); } } else { // (if there is no entry, then there are no borrows to be tracked) @@ -175,11 +200,14 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { mir::StatementKind::Nop => {} } + + self.kill_loans_out_of_scope_at_location(sets, location); } + fn terminator_effect(&self, - _sets: &mut BlockSets, - _location: Location) { - // no terminators start nor end region scopes. + sets: &mut BlockSets, + location: Location) { + self.kill_loans_out_of_scope_at_location(sets, location); } fn propagate_call_return(&self, diff --git a/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs b/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs new file mode 100644 index 00000000000..c02977f22ea --- /dev/null +++ b/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs @@ -0,0 +1,50 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// compile-flags:-Zborrowck-mir -Znll + +#![allow(warnings)] +#![feature(rustc_attrs)] + + +fn main() { +} + +fn nll_fail() { + let mut data = ('a', 'b', 'c'); + let c = &mut data.0; + capitalize(c); + data.0 = 'e'; + //~^ ERROR (Ast) [E0506] + //~| ERROR (Mir) [E0506] + data.0 = 'f'; + //~^ ERROR (Ast) [E0506] + //~| ERROR (Mir) [E0506] + data.0 = 'g'; + //~^ ERROR (Ast) [E0506] + //~| ERROR (Mir) [E0506] + capitalize(c); +} + +fn nll_ok() { + let mut data = ('a', 'b', 'c'); + let c = &mut data.0; + capitalize(c); + data.0 = 'e'; + //~^ ERROR (Ast) [E0506] + data.0 = 'f'; + //~^ ERROR (Ast) [E0506] + data.0 = 'g'; + //~^ ERROR (Ast) [E0506] +} + +fn capitalize(_: &mut char) { +} diff --git a/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs b/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs new file mode 100644 index 00000000000..5e3a003b54e --- /dev/null +++ b/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs @@ -0,0 +1,49 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// compile-flags:-Zborrowck-mir -Znll + +#![allow(warnings)] +#![feature(rustc_attrs)] + +fn main() { +} + +fn nll_fail() { + let mut data = vec!['a', 'b', 'c']; + let slice = &mut data; + capitalize(slice); + data.push('d'); + //~^ ERROR (Ast) [E0499] + //~| ERROR (Mir) [E0499] + data.push('e'); + //~^ ERROR (Ast) [E0499] + //~| ERROR (Mir) [E0499] + data.push('f'); + //~^ ERROR (Ast) [E0499] + //~| ERROR (Mir) [E0499] + capitalize(slice); +} + +fn nll_ok() { + let mut data = vec!['a', 'b', 'c']; + let slice = &mut data; + capitalize(slice); + data.push('d'); + //~^ ERROR (Ast) [E0499] + data.push('e'); + //~^ ERROR (Ast) [E0499] + data.push('f'); + //~^ ERROR (Ast) [E0499] +} + +fn capitalize(_: &mut [char]) { +} diff --git a/src/test/compile-fail/nll/region-ends-after-if-condition.rs b/src/test/compile-fail/nll/region-ends-after-if-condition.rs new file mode 100644 index 00000000000..dbc35fa99b0 --- /dev/null +++ b/src/test/compile-fail/nll/region-ends-after-if-condition.rs @@ -0,0 +1,32 @@ +// 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 or the MIT license +// , 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:-Zborrowck-mir -Znll + +#![allow(warnings)] +#![feature(rustc_attrs)] + +struct MyStruct { + field: String +} + +fn main() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &my_struct.field; + if value.is_empty() { + my_struct.field.push_str("Hello, world!"); + //~^ ERROR cannot borrow (Ast) + } +} diff --git a/src/test/compile-fail/nll/return_from_loop.rs b/src/test/compile-fail/nll/return_from_loop.rs new file mode 100644 index 00000000000..6b287fd2272 --- /dev/null +++ b/src/test/compile-fail/nll/return_from_loop.rs @@ -0,0 +1,49 @@ +// 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 or the MIT license +// , 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:-Zborrowck-mir -Znll + +#![allow(warnings)] +#![feature(rustc_attrs)] + +struct MyStruct { + field: String +} + +fn main() { +} + +fn nll_fail() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &mut my_struct.field; + loop { + my_struct.field.push_str("Hello, world!"); + //~^ ERROR (Ast) [E0499] + //~| ERROR (Mir) [E0499] + value.len(); + return; + } +} + +fn nll_ok() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &mut my_struct.field; + loop { + my_struct.field.push_str("Hello, world!"); + //~^ ERROR (Ast) [E0499] + return; + } +} From f27eb1e684e7add8362afb8e019af7a36a860cf1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 30 Oct 2017 09:01:12 -0400 Subject: [PATCH 27/33] change region display to `'_#Nr`, update the `newtype_index!` macro The macro now takes a format string. It no longer defaults to using the type name. Didn't seem worth going through contortions to maintain. I also changed most of the debug formats to be `foo[N]` instead of `fooN`. --- src/librustc/middle/region.rs | 2 +- src/librustc/mir/mod.rs | 10 +++--- src/librustc_data_structures/indexed_vec.rs | 35 ++++++++++--------- src/librustc_mir/lib.rs | 1 - src/librustc_mir/transform/nll/mod.rs | 2 +- .../end_region_destruction_extents_1.rs | 4 +-- src/test/mir-opt/nll/reborrow-basic.rs | 4 +-- src/test/mir-opt/nll/region-liveness-basic.rs | 4 +-- .../nll/region-liveness-drop-may-dangle.rs | 2 +- .../nll/region-liveness-drop-no-may-dangle.rs | 2 +- .../nll/region-liveness-two-disjoint-uses.rs | 6 ++-- .../mir-opt/nll/region-subtyping-basic.rs | 6 ++-- 12 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index fa4ee7c0092..e725592ff99 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -158,7 +158,7 @@ pub struct BlockRemainder { newtype_index!(FirstStatementIndex { - DEBUG_NAME = "", + DEBUG_FORMAT = "{}", MAX = SCOPE_DATA_REMAINDER_MAX, }); diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 307637b2f1d..c4a33bb07cd 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -417,7 +417,7 @@ pub enum BorrowKind { newtype_index!(Local { - DEBUG_NAME = "_", + DEBUG_FORMAT = "_{}", const RETURN_POINTER = 0, }); @@ -553,7 +553,7 @@ pub struct UpvarDecl { /////////////////////////////////////////////////////////////////////////// // BasicBlock -newtype_index!(BasicBlock { DEBUG_NAME = "bb" }); +newtype_index!(BasicBlock { DEBUG_FORMAT = "bb{}" }); /////////////////////////////////////////////////////////////////////////// // BasicBlockData and Terminator @@ -1135,7 +1135,7 @@ pub type LvalueProjection<'tcx> = Projection<'tcx, Lvalue<'tcx>, Local, Ty<'tcx> /// and the index is a local. pub type LvalueElem<'tcx> = ProjectionElem<'tcx, Local, Ty<'tcx>>; -newtype_index!(Field { DEBUG_NAME = "field" }); +newtype_index!(Field { DEBUG_FORMAT = "field[{}]" }); impl<'tcx> Lvalue<'tcx> { pub fn field(self, f: Field, ty: Ty<'tcx>) -> Lvalue<'tcx> { @@ -1202,7 +1202,7 @@ impl<'tcx> Debug for Lvalue<'tcx> { newtype_index!(VisibilityScope { - DEBUG_NAME = "scope", + DEBUG_FORMAT = "scope[{}]", const ARGUMENT_VISIBILITY_SCOPE = 0, }); @@ -1529,7 +1529,7 @@ pub struct Constant<'tcx> { pub literal: Literal<'tcx>, } -newtype_index!(Promoted { DEBUG_NAME = "promoted" }); +newtype_index!(Promoted { DEBUG_FORMAT = "promoted[{}]" }); #[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum Literal<'tcx> { diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index 1d1b367de20..0660cd96a4a 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -47,7 +47,7 @@ macro_rules! newtype_index { newtype_index!( @type[$name] @max[::std::u32::MAX] - @debug_name[unsafe {::std::intrinsics::type_name::<$name>() }]); + @debug_format["{}"]); ); // Define any constants @@ -55,14 +55,14 @@ macro_rules! newtype_index { newtype_index!( @type[$name] @max[::std::u32::MAX] - @debug_name[unsafe {::std::intrinsics::type_name::<$name>() }] + @debug_format["{}"] $($tokens)+); ); // ---- private rules ---- // Base case, user-defined constants (if any) have already been defined - (@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr]) => ( + (@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr]) => ( #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, RustcEncodable, RustcDecodable)] pub struct $type(pub u32); @@ -79,40 +79,43 @@ macro_rules! newtype_index { impl ::std::fmt::Debug for $type { fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(fmt, "{}{}", $debug_name, self.0) + write!(fmt, $debug_format, self.0) } } ); // Rewrite final without comma to one that includes comma - (@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr] + (@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr] $name:ident = $constant:expr) => ( - newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $name = $constant,); + newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $name = $constant,); ); // Rewrite final const without comma to one that includes comma - (@type[$type:ident] @max[$_max:expr] @debug_name[$debug_name:expr] + (@type[$type:ident] @max[$_max:expr] @debug_format[$debug_format:expr] const $name:ident = $constant:expr) => ( - newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] const $name = $constant,); + newtype_index!(@type[$type] + @max[$max] + @debug_format[$debug_format] + const $name = $constant,); ); // Replace existing default for max - (@type[$type:ident] @max[$_max:expr] @debug_name[$debug_name:expr] + (@type[$type:ident] @max[$_max:expr] @debug_format[$debug_format:expr] MAX = $max:expr, $($tokens:tt)*) => ( - newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*); + newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*); ); - // Replace existing default for debug_name - (@type[$type:ident] @max[$max:expr] @debug_name[$_debug_name:expr] - DEBUG_NAME = $debug_name:expr, $($tokens:tt)*) => ( - newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*); + // Replace existing default for debug_format + (@type[$type:ident] @max[$max:expr] @debug_format[$_debug_format:expr] + DEBUG_FORMAT = $debug_format:expr, $($tokens:tt)*) => ( + newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*); ); // Assign a user-defined constant (as final param) - (@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr] + (@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr] const $name:ident = $constant:expr, $($tokens:tt)*) => ( pub const $name: $type = $type($constant); - newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*); + newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*); ); } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 16753cee7e0..66de35bd7f5 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -20,7 +20,6 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(box_syntax)] #![feature(conservative_impl_trait)] #![feature(const_fn)] -#![feature(core_intrinsics)] #![feature(i128_type)] #![feature(rustc_diagnostic_macros)] #![feature(placement_in_syntax)] diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 7df217c62e3..66a9da2c72a 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -192,7 +192,7 @@ impl Region { } newtype_index!(RegionIndex { - DEBUG_NAME = "R", + DEBUG_FORMAT = "'_#{}r", }); /// Right now, we piggy back on the `ReVar` to store our NLL inference diff --git a/src/test/mir-opt/end_region_destruction_extents_1.rs b/src/test/mir-opt/end_region_destruction_extents_1.rs index 1f9ad988acc..61dc1d20659 100644 --- a/src/test/mir-opt/end_region_destruction_extents_1.rs +++ b/src/test/mir-opt/end_region_destruction_extents_1.rs @@ -133,11 +133,11 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> { // StorageLive(_3); // StorageLive(_4); // StorageLive(_5); -// _5 = promoted1; +// _5 = promoted[1]; // _4 = &'12ds (*_5); // StorageLive(_7); // StorageLive(_8); -// _8 = promoted0; +// _8 = promoted[0]; // _7 = &'10s (*_8); // _3 = D1<'12ds, '10s>::{{constructor}}(_4, _7); // EndRegion('10s); diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs index 1983478a4e1..60a4da430b9 100644 --- a/src/test/mir-opt/nll/reborrow-basic.rs +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -28,9 +28,9 @@ fn main() { // END RUST SOURCE // START rustc.node13.nll.0.mir -// | R5: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#5r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} // ... -// | R7: {bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#7r: {bb0[11], bb0[12], bb0[13], bb0[14]} // END rustc.node13.nll.0.mir // START rustc.node13.nll.0.mir // let _2: &'_#5r mut i32; diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index fa4eb9627db..7792f0a36f3 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -31,8 +31,8 @@ fn main() { // END RUST SOURCE // START rustc.node12.nll.0.mir -// | R0: {bb1[1], bb2[0], bb2[1]} -// | R1: {bb1[1], bb2[0], bb2[1]} +// | '_#0r: {bb1[1], bb2[0], bb2[1]} +// | '_#1r: {bb1[1], bb2[0], bb2[1]} // ... // let _2: &'_#1r usize; // 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 index 7482288e911..4f4bb596e5f 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs @@ -44,5 +44,5 @@ unsafe impl<#[may_dangle] T> Drop for Wrap { // END RUST SOURCE // START rustc.node12.nll.0.mir -// | R4: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]} +// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]} // END rustc.node12.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs index 5f4da966281..20865fdfe9b 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -46,5 +46,5 @@ impl Drop for Wrap { // END RUST SOURCE // START rustc.node12.nll.0.mir -// | R4: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb8[0]} +// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb8[0]} // END rustc.node12.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs index eb904af39ac..1387fe52563 100644 --- a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -36,10 +36,10 @@ fn main() { // END RUST SOURCE // START rustc.node12.nll.0.mir -// | R0: {bb1[1], bb2[0], bb2[1]} +// | '_#0r: {bb1[1], bb2[0], bb2[1]} // ... -// | R2: {bb7[2], bb7[3], bb7[4]} -// | R3: {bb1[1], bb2[0], bb2[1], bb7[2], bb7[3], bb7[4]} +// | '_#2r: {bb7[2], bb7[3], bb7[4]} +// | '_#3r: {bb1[1], bb2[0], bb2[1], bb7[2], bb7[3], bb7[4]} // ... // let mut _2: &'_#3r usize; // ... diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index bc97858e03d..4ae891f5b70 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -32,9 +32,9 @@ fn main() { // END RUST SOURCE // START rustc.node12.nll.0.mir -// | R0: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} -// | R1: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} -// | R2: {bb1[5], bb1[6], bb2[0], bb2[1]} +// | '_#0r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} +// | '_#1r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} +// | '_#2r: {bb1[5], bb1[6], bb2[0], bb2[1]} // END rustc.node12.nll.0.mir // START rustc.node12.nll.0.mir // let _2: &'_#1r usize; From bf57a230eb94b8a513f3c7b1f68ea5779bcb281a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 30 Oct 2017 10:59:14 -0400 Subject: [PATCH 28/33] WIP patch `compile-fail/nll/region-ends-after-if-condition.rs` --- .../nll/region-ends-after-if-condition.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/test/compile-fail/nll/region-ends-after-if-condition.rs b/src/test/compile-fail/nll/region-ends-after-if-condition.rs index dbc35fa99b0..bec56982c57 100644 --- a/src/test/compile-fail/nll/region-ends-after-if-condition.rs +++ b/src/test/compile-fail/nll/region-ends-after-if-condition.rs @@ -21,12 +21,26 @@ struct MyStruct { field: String } -fn main() { +fn foo1() { let mut my_struct = MyStruct { field: format!("Hello") }; let value = &my_struct.field; if value.is_empty() { my_struct.field.push_str("Hello, world!"); - //~^ ERROR cannot borrow (Ast) + //~^ ERROR (Ast) [E0502] } } + +fn foo2() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &my_struct.field; + if value.is_empty() { + my_struct.field.push_str("Hello, world!"); + //~^ ERROR (Ast) [E0502] + //~| ERROR (Mir) [E0502] + } + drop(value); +} + +fn main() { } From 7675ea997a72e5eca54baf72210c2c5db41fce62 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 30 Oct 2017 16:19:24 -0400 Subject: [PATCH 29/33] remove the NLL pass (it is now invoked by mir borrowck) --- src/librustc_driver/driver.rs | 1 - src/librustc_mir/transform/nll/mod.rs | 25 ++----------------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index cd3740f72e0..ea3411f4d07 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -1003,7 +1003,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants); passes.push_pass(MIR_VALIDATED, mir::transform::simplify::SimplifyCfg::new("qualify-consts")); - passes.push_pass(MIR_VALIDATED, mir::transform::nll::NLL); // borrowck runs between MIR_VALIDATED and MIR_OPTIMIZED. diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 66a9da2c72a..d4938dc40bf 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::ty::{self, RegionKind, TyCtxt}; +use rustc::ty::{self, RegionKind}; use rustc::mir::{Location, Mir}; -use rustc::mir::transform::{MirPass, MirSource}; +use rustc::mir::transform::MirSource; use rustc::infer::InferCtxt; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_vec::Idx; @@ -29,27 +29,6 @@ use self::region_infer::RegionInferenceContext; mod renumber; -// MIR Pass for non-lexical lifetimes -pub struct NLL; - -impl MirPass for NLL { - fn run_pass<'a, 'tcx>( - &self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - source: MirSource, - input_mir: &mut Mir<'tcx>, - ) { - if !tcx.sess.opts.debugging_opts.nll { - return; - } - - tcx.infer_ctxt().enter(|ref infcx| { - let mut mir = input_mir.clone(); - let _ = compute_regions(infcx, source, &mut mir); - }); - } -} - /// Computes the (non-lexical) regions from the input MIR. /// /// This may result in errors being reported. From 99aa313636105ef4f79dde1ee566caff6bca41ca Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 31 Oct 2017 11:41:42 -0400 Subject: [PATCH 30/33] runtest: fix mir directory and delete outdated MIR files --- src/tools/compiletest/src/runtest.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 0473c2a5405..f8628158aff 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1404,6 +1404,7 @@ actual:\n\ "-Zdump-mir-exclude-pass-number"]); let mir_dump_dir = self.get_mir_dump_dir(); + let _ = fs::remove_dir_all(&mir_dump_dir); create_dir_all(mir_dump_dir.as_path()).unwrap(); let mut dir_opt = "-Zdump-mir-dir=".to_string(); dir_opt.push_str(mir_dump_dir.to_str().unwrap()); @@ -2367,12 +2368,10 @@ actual:\n\ } fn get_mir_dump_dir(&self) -> PathBuf { - let mut mir_dump_dir = PathBuf::from(self.config.build_base - .as_path() - .to_str() - .unwrap()); + let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path()); debug!("input_file: {:?}", self.testpaths.file); - mir_dump_dir.push(self.testpaths.file.file_stem().unwrap().to_str().unwrap()); + mir_dump_dir.push(&self.testpaths.relative_dir); + mir_dump_dir.push(self.testpaths.file.file_stem().unwrap()); mir_dump_dir } From d9fb792d7c259b76e51c1cbd6a7e5f9cee27ff99 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 31 Oct 2017 11:41:54 -0400 Subject: [PATCH 31/33] treat -Znll as implying -Zborrowck-mir --- src/librustc_mir/borrow_check.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index 8b5f1539ac0..819f67a39e9 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -51,7 +51,11 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { let src = MirSource::from_local_def_id(tcx, def_id); debug!("run query mir_borrowck: {}", tcx.node_path_str(src.item_id())); - if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.debugging_opts.borrowck_mir { + if { + !tcx.has_attr(def_id, "rustc_mir_borrowck") && + !tcx.sess.opts.debugging_opts.borrowck_mir && + !tcx.sess.opts.debugging_opts.nll + } { return; } From 9b3af6ce3c61e6076ed61b52542c037d0296f062 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 31 Oct 2017 12:55:54 -0400 Subject: [PATCH 32/33] rewrite `write_mir_fn_graphviz` to cope with non-global tcx --- src/librustc_mir/lib.rs | 1 + src/librustc_mir/util/graphviz.rs | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 66de35bd7f5..b72823dff2b 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -25,6 +25,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(placement_in_syntax)] #![feature(collection_placement)] #![feature(nonzero)] +#![feature(underscore_lifetimes)] #[macro_use] extern crate bitflags; diff --git a/src/librustc_mir/util/graphviz.rs b/src/librustc_mir/util/graphviz.rs index cec8067530b..a38d317b823 100644 --- a/src/librustc_mir/util/graphviz.rs +++ b/src/librustc_mir/util/graphviz.rs @@ -21,10 +21,10 @@ use rustc_data_structures::indexed_vec::Idx; use super::pretty::dump_mir_def_ids; /// Write a graphviz DOT graph of a list of MIRs. -pub fn write_mir_graphviz<'a, 'tcx, W>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - single: Option, - w: &mut W) - -> io::Result<()> +pub fn write_mir_graphviz<'tcx, W>(tcx: TyCtxt<'_, '_, 'tcx>, + single: Option, + w: &mut W) + -> io::Result<()> where W: Write { for def_id in dump_mir_def_ids(tcx, single) { @@ -36,10 +36,10 @@ pub fn write_mir_graphviz<'a, 'tcx, W>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } /// Write a graphviz DOT graph of the MIR. -pub fn write_mir_fn_graphviz<'a, 'tcx, W>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - nodeid: NodeId, - mir: &Mir, - w: &mut W) -> io::Result<()> +pub fn write_mir_fn_graphviz<'tcx, W>(tcx: TyCtxt<'_, '_, 'tcx>, + nodeid: NodeId, + mir: &Mir, + w: &mut W) -> io::Result<()> where W: Write { writeln!(w, "digraph Mir_{} {{", nodeid)?; @@ -137,11 +137,11 @@ fn write_edges(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result /// 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<()> { +fn write_graph_label<'a, 'gcx, 'tcx, W: Write>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + nid: NodeId, + mir: &Mir, + w: &mut W) + -> io::Result<()> { write!(w, " label= Date: Tue, 31 Oct 2017 14:02:31 -0400 Subject: [PATCH 33/33] patch mir-opt reference files --- src/test/mir-opt/nll/liveness-call-subtlety.rs | 16 +++++++++++----- .../nll/region-liveness-drop-no-may-dangle.rs | 2 +- .../nll/region-liveness-two-disjoint-uses.rs | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs index 551aff8454a..873431505f5 100644 --- a/src/test/mir-opt/nll/liveness-call-subtlety.rs +++ b/src/test/mir-opt/nll/liveness-call-subtlety.rs @@ -31,15 +31,21 @@ fn main() { // | Live variables at bb0[0]: [] // StorageLive(_1); // | Live variables at bb0[1]: [] -// _1 = const >::new(const 22usize) -> bb1; +// StorageLive(_2); +// | Live variables at bb0[2]: [] +// _2 = const 22usize; +// | Live variables at bb0[3]: [_2] +// _1 = const >::new(_2) -> bb1; // } // END rustc.node12.nll.0.mir // START rustc.node12.nll.0.mir // | Live variables on entry to bb1: [_1 (drop)] // bb1: { -// | Live variables at bb1[0]: [_1 (drop)] -// StorageLive(_2); -// | Live variables at bb1[1]: [_1 (drop)] -// _2 = const can_panic() -> [return: bb2, unwind: bb4]; +// | Live variables at bb1[0]: [_1 (drop)] +// StorageDead(_2); +// | Live variables at bb1[1]: [_1 (drop)] +// StorageLive(_3); +// | Live variables at bb1[2]: [_1 (drop)] +// _3 = const can_panic() -> [return: bb2, unwind: bb4]; // } // END rustc.node12.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs index 20865fdfe9b..0ddb745b61f 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -46,5 +46,5 @@ impl Drop for Wrap { // END RUST SOURCE // START rustc.node12.nll.0.mir -// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb8[0]} +// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb3[1], bb3[2], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb7[2], bb8[0]} // END rustc.node12.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs index 1387fe52563..664298b9374 100644 --- a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -45,5 +45,5 @@ fn main() { // ... // _2 = &'_#0r _1[_3]; // ... -// _2 = &'_#2r (*_10); +// _2 = &'_#2r (*_11); // END rustc.node12.nll.0.mir