Skip to content

Commit

Permalink
Port borrows across yield check to MIR borrowck
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoxc committed Jan 23, 2018
1 parent 410d27b commit 55c6c88
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/librustc_mir/borrow_check/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
}

// Retrieve span of given borrow from the current MIR representation
fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
pub fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
self.mir.source_info(borrow.location).span
}

Expand Down
61 changes: 61 additions & 0 deletions src/librustc_mir/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,21 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
};
let flow_inits = flow_inits; // remove mut

let movable_generator = !match tcx.hir.get(id) {
hir::map::Node::NodeExpr(&hir::Expr {
node: hir::ExprClosure(.., Some(hir::GeneratorMovability::Static)),
..
}) => true,
_ => false,
};

let mut mbcx = MirBorrowckCtxt {
tcx: tcx,
mir: mir,
node_id: id,
move_data: &mdpe.move_data,
param_env: param_env,
movable_generator,
locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) {
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => false,
hir::BodyOwnerKind::Fn => true,
Expand Down Expand Up @@ -277,6 +286,7 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
node_id: ast::NodeId,
move_data: &'cx MoveData<'tcx>,
param_env: ParamEnv<'gcx>,
movable_generator: bool,
/// This keeps track of whether local variables are free-ed when the function
/// exits even without a `StorageDead`, which appears to be the case for
/// constants.
Expand Down Expand Up @@ -534,6 +544,18 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
drop: _,
} => {
self.consume_operand(ContextKind::Yield.new(loc), (value, span), flow_state);

if self.movable_generator {
// Look for any active borrows to locals
let domain = flow_state.borrows.operator();
let data = domain.borrows();
flow_state.borrows.with_elems_outgoing(|borrows| {
for i in borrows {
let borrow = &data[i.borrow_index()];
self.check_for_local_borrow(borrow, span);
}
});
}
}

TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {
Expand Down Expand Up @@ -1099,6 +1121,45 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
}
}

/// Reports an error if this is a borrow of local data.
/// This is called for all Yield statements on movable generators
fn check_for_local_borrow(
&mut self,
borrow: &BorrowData<'tcx>,
yield_span: Span)
{
fn borrow_of_local_data<'tcx>(place: &Place<'tcx>) -> bool {
match place {
Place::Static(..) => false,
Place::Local(..) => true,
Place::Projection(box proj) => {
match proj.elem {
// Reborrow of already borrowed data is ignored
// Any errors will be caught on the initial borrow
ProjectionElem::Deref => false,

// For interior references and downcasts, find out if the base is local
ProjectionElem::Field(..) |
ProjectionElem::Index(..) |
ProjectionElem::ConstantIndex { .. } |
ProjectionElem::Subslice { .. } |
ProjectionElem::Downcast(..) => {
borrow_of_local_data(&proj.base)
}
}
}
}
}

debug!("check_for_local_borrow({:?})", borrow);

if borrow_of_local_data(&borrow.borrowed_place) {
self.tcx.cannot_borrow_across_generator_yield(self.retrieve_borrow_span(borrow),
yield_span,
Origin::Mir).emit();
}
}

fn check_activations(
&mut self,
location: Location,
Expand Down
25 changes: 25 additions & 0 deletions src/test/ui/generator/generator-with-nll.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2018 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: -Z borrowck=compare

#![feature(generators)]
#![feature(nll)]

fn main() {
|| {
// The reference in `_a` is a Legal with NLL since it ends before the yield
let _a = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast)
let b = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast)
//~^ borrow may still be in use when generator yields (Mir)
yield ();
println!("{}", b);
};
}
29 changes: 29 additions & 0 deletions src/test/ui/generator/generator-with-nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
error[E0626]: borrow may still be in use when generator yields (Mir)
--> $DIR/generator-with-nll.rs:20:17
|
20 | let b = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast)
| ^^^^^^^^^
21 | //~^ borrow may still be in use when generator yields (Mir)
22 | yield ();
| -------- possible yield occurs here

error[E0626]: borrow may still be in use when generator yields (Ast)
--> $DIR/generator-with-nll.rs:19:23
|
19 | let _a = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast)
| ^^^^
...
22 | yield ();
| -------- possible yield occurs here

error[E0626]: borrow may still be in use when generator yields (Ast)
--> $DIR/generator-with-nll.rs:20:22
|
20 | let b = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast)
| ^^^^
21 | //~^ borrow may still be in use when generator yields (Mir)
22 | yield ();
| -------- possible yield occurs here

error: aborting due to 3 previous errors

0 comments on commit 55c6c88

Please sign in to comment.