connect MIR borrowck with NLL
This commit is contained in:
parent
81449174f3
commit
a94b01a0e2
6 changed files with 232 additions and 10 deletions
|
@ -17,7 +17,8 @@ use rustc::ty::maps::Providers;
|
||||||
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue, Local};
|
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue, Local};
|
||||||
use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
|
use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
|
||||||
use rustc::mir::{Statement, StatementKind, Terminator, TerminatorKind};
|
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_set::{self, IdxSetBuf};
|
||||||
use rustc_data_structures::indexed_vec::{Idx};
|
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>,
|
fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||||
mir: &Mir<'gcx>,
|
input_mir: &Mir<'gcx>,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
src: MirSource)
|
src: MirSource)
|
||||||
{
|
{
|
||||||
|
@ -72,7 +73,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||||
|
|
||||||
let id = src.item_id();
|
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,
|
Ok(move_data) => move_data,
|
||||||
Err((move_data, move_errors)) => {
|
Err((move_data, move_errors)) => {
|
||||||
for move_error in 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 mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
|
||||||
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
|
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
|
||||||
let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
|
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));
|
|bd, i| bd.location(i));
|
||||||
let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
|
let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
|
||||||
MaybeInitializedLvals::new(tcx, mir, &mdpe),
|
MaybeInitializedLvals::new(tcx, mir, &mdpe),
|
||||||
|
|
|
@ -21,6 +21,8 @@ use rustc_data_structures::indexed_vec::{IndexVec};
|
||||||
|
|
||||||
use dataflow::{BitDenotation, BlockSets, DataflowOperator};
|
use dataflow::{BitDenotation, BlockSets, DataflowOperator};
|
||||||
pub use dataflow::indexes::BorrowIndex;
|
pub use dataflow::indexes::BorrowIndex;
|
||||||
|
use transform::nll::region_infer::RegionInferenceContext;
|
||||||
|
use transform::nll::ToRegionIndex;
|
||||||
|
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
|
|
||||||
|
@ -36,6 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||||
location_map: FxHashMap<Location, BorrowIndex>,
|
location_map: FxHashMap<Location, BorrowIndex>,
|
||||||
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
|
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
|
||||||
region_span_map: FxHashMap<RegionKind, Span>,
|
region_span_map: FxHashMap<RegionKind, Span>,
|
||||||
|
nonlexical_regioncx: Option<&'a RegionInferenceContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporarily allow some dead fields: `kind` and `region` will be
|
// 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> {
|
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(),
|
let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
|
||||||
location_map: FxHashMap(),
|
location_map: FxHashMap(),
|
||||||
region_map: FxHashMap(),
|
region_map: FxHashMap(),
|
||||||
|
@ -75,7 +81,8 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
|
||||||
borrows: visitor.idx_vec,
|
borrows: visitor.idx_vec,
|
||||||
location_map: visitor.location_map,
|
location_map: visitor.location_map,
|
||||||
region_map: visitor.region_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> {
|
struct GatherBorrows<'tcx> {
|
||||||
idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
|
idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
|
||||||
|
@ -121,9 +128,26 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
|
||||||
/// meaning there. Otherwise, it should return some.
|
/// meaning there. Otherwise, it should return some.
|
||||||
pub fn opt_region_end_span(&self, region: &Region) -> Option<Span> {
|
pub fn opt_region_end_span(&self, region: &Region) -> Option<Span> {
|
||||||
let opt_span = self.region_span_map.get(region);
|
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())
|
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<BorrowIndex>,
|
||||||
|
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> {
|
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 {
|
match stmt.kind {
|
||||||
mir::StatementKind::EndRegion(region_scope) => {
|
mir::StatementKind::EndRegion(region_scope) => {
|
||||||
if let Some(borrow_indexes) = self.region_map.get(&ReScope(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); }
|
for idx in borrow_indexes { sets.kill(&idx); }
|
||||||
} else {
|
} else {
|
||||||
// (if there is no entry, then there are no borrows to be tracked)
|
// (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 => {}
|
mir::StatementKind::Nop => {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.kill_loans_out_of_scope_at_location(sets, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(&self,
|
fn terminator_effect(&self,
|
||||||
_sets: &mut BlockSets<BorrowIndex>,
|
sets: &mut BlockSets<BorrowIndex>,
|
||||||
_location: Location) {
|
location: Location) {
|
||||||
// no terminators start nor end region scopes.
|
self.kill_loans_out_of_scope_at_location(sets, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn propagate_call_return(&self,
|
fn propagate_call_return(&self,
|
||||||
|
|
50
src/test/compile-fail/nll/loan_ends_mid_block_pair.rs
Normal file
50
src/test/compile-fail/nll/loan_ends_mid_block_pair.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
}
|
49
src/test/compile-fail/nll/loan_ends_mid_block_vec.rs
Normal file
49
src/test/compile-fail/nll/loan_ends_mid_block_vec.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
|
||||||
|
// 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]) {
|
||||||
|
}
|
32
src/test/compile-fail/nll/region-ends-after-if-condition.rs
Normal file
32
src/test/compile-fail/nll/region-ends-after-if-condition.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Basic test for liveness constraints: the region (`R1`) that appears
|
||||||
|
// in the type of `p` includes the points after `&v[0]` up to (but not
|
||||||
|
// including) the call to `use_x`. The `else` branch is not included.
|
||||||
|
|
||||||
|
// compile-flags:-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)
|
||||||
|
}
|
||||||
|
}
|
49
src/test/compile-fail/nll/return_from_loop.rs
Normal file
49
src/test/compile-fail/nll/return_from_loop.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Basic test for liveness constraints: the region (`R1`) that appears
|
||||||
|
// in the type of `p` includes the points after `&v[0]` up to (but not
|
||||||
|
// including) the call to `use_x`. The `else` branch is not included.
|
||||||
|
|
||||||
|
// compile-flags:-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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue