diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 61e7eb5933915..a8af5f5d32b97 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -12,6 +12,7 @@ use rustc::hir; use rustc::hir::def_id::DefId; +use rustc::hir::map::definitions::DefPathData; use rustc::infer::InferCtxt; use rustc::ty::{self, ParamEnv, TyCtxt}; use rustc::ty::maps::Providers; @@ -131,6 +132,12 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( move_data: move_data, param_env: param_env, }; + let body_id = match tcx.def_key(def_id).disambiguated_data.data { + DefPathData::StructCtor | + DefPathData::EnumVariant(_) => None, + _ => Some(tcx.hir.body_owned_by(id)) + }; + let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); let mut flow_inits = FlowInProgress::new(do_dataflow( tcx, @@ -206,7 +213,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( id, &attributes, &dead_unwinds, - Borrows::new(tcx, mir, opt_regioncx), + Borrows::new(tcx, mir, opt_regioncx, def_id, body_id), |bd, i| bd.location(i), )); diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index d1bb1f39e221c..932aad0bb1d84 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -51,6 +51,17 @@ impl<'tcx> CFG<'tcx> { source_info: SourceInfo, region_scope: region::Scope) { if tcx.sess.emit_end_regions() { + if let region::ScopeData::CallSite(_) = region_scope.data() { + // The CallSite scope (aka the root scope) is sort of weird, in that it is + // supposed to "separate" the "interior" and "exterior" of a closure. Being + // that, it is not really a part of the region hierarchy, but for some + // reason it *is* considered a part of it. + // + // It should die a hopefully painful death with NLL, so let's leave this hack + // for now so that nobody can complain about soundness. + return + } + self.push(block, Statement { source_info, kind: StatementKind::EndRegion(region_scope), diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 2bba3e263f3c6..b3b06f7f6cdb0 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use rustc::hir; +use rustc::hir::def_id::DefId; +use rustc::middle::region; use rustc::mir::{self, Location, Mir}; use rustc::mir::visit::Visitor; use rustc::ty::{self, Region, TyCtxt}; @@ -27,6 +30,7 @@ use borrow_check::nll::ToRegionVid; use syntax_pos::Span; use std::fmt; +use std::rc::Rc; // `Borrows` maps each dataflow bit to an `Rvalue::Ref`, which can be // uniquely identified in the MIR by the `Location` of the assigment @@ -34,6 +38,8 @@ use std::fmt; pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> { tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, + scope_tree: Rc, + root_scope: Option, borrows: IndexVec>, location_map: FxHashMap, region_map: FxHashMap, FxHashSet>, @@ -69,8 +75,14 @@ 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>, - nonlexical_regioncx: Option>) + nonlexical_regioncx: Option>, + def_id: DefId, + body_id: Option) -> Self { + let scope_tree = tcx.region_scope_tree(def_id); + let root_scope = body_id.map(|body_id| { + region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id) + }); let mut visitor = GatherBorrows { tcx, mir, @@ -83,6 +95,8 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { return Borrows { tcx: tcx, mir: mir, borrows: visitor.idx_vec, + scope_tree, + root_scope, location_map: visitor.location_map, region_map: visitor.region_map, region_span_map: visitor.region_span_map, @@ -253,8 +267,17 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { // like unwind paths, we do not always emit `EndRegion` statements, so we // add some kills here as a "backup" and to avoid spurious error messages. for (borrow_index, borrow_data) in self.borrows.iter_enumerated() { - if let ReScope(..) = borrow_data.region { - sets.kill(&borrow_index); + if let ReScope(scope) = borrow_data.region { + // Check that the scope is not actually a scope from a function that is + // a parent of our closure. Note that the CallSite scope itself is + // *outside* of the closure, for some weird reason. + if let Some(root_scope) = self.root_scope { + if *scope != root_scope && + self.scope_tree.is_subscope_of(*scope, root_scope) + { + sets.kill(&borrow_index); + } + } } } } diff --git a/src/test/compile-fail/region-borrow-params-issue-29793-big.rs b/src/test/compile-fail/region-borrow-params-issue-29793-big.rs index 887f7836ee143..a4dc00bd2b1e7 100644 --- a/src/test/compile-fail/region-borrow-params-issue-29793-big.rs +++ b/src/test/compile-fail/region-borrow-params-issue-29793-big.rs @@ -16,6 +16,10 @@ // behavior (because the improperly accepted closure was actually // able to be invoked). +// ignore-tidy-linelength +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + struct WrapA(Option); impl WrapA { @@ -75,9 +79,11 @@ impl WrapA fn main() { let mut w = WrapA::new().set(|x: usize, y: usize| { WrapB::new().set(|t: bool| if t { x } else { y }) // (separate errors for `x` vs `y`) - //~^ ERROR `x` does not live long enough - //~| ERROR `y` does not live long enough + //[ast]~^ ERROR `x` does not live long enough + //[ast]~| ERROR `y` does not live long enough }); + //[mir]~^ ERROR borrowed value does not live long enough + //[mir]~| ERROR borrowed value does not live long enough w.handle(); // This works // w.handle_ref(); // This doesn't