From a94b01a0e244a1df75f90cd8f0bfee546b4b2690 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 30 Oct 2017 08:28:07 -0400 Subject: [PATCH] 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; + } +}