Allow individual upvars to be inferred to move semantics. Fixes #21603.

This commit is contained in:
Niko Matsakis 2015-01-24 17:25:07 -05:00
parent 2f29cdeb4b
commit 80c793c1d3
7 changed files with 167 additions and 7 deletions

View file

@ -366,6 +366,9 @@ fn delegate_consume(&mut self,
consume_id: ast::NodeId,
consume_span: Span,
cmt: mc::cmt<'tcx>) {
debug!("delegate_consume(consume_id={}, cmt={})",
consume_id, cmt.repr(self.tcx()));
let mode = copy_or_move(self.typer, &cmt, DirectRefMove);
self.delegate.consume(consume_id, consume_span, cmt, mode);
}

View file

@ -305,7 +305,7 @@ fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult<Ty<'tcx>> {
}
fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool {
let ty = self.infcx().resolve_type_vars_if_possible(&ty);
traits::type_known_to_meet_builtin_bound(self.infcx(), self, ty, ty::BoundCopy, span)
!traits::type_known_to_meet_builtin_bound(self.infcx(), self, ty, ty::BoundCopy, span)
}
fn node_method_ty(&self, method_call: ty::MethodCall)
-> Option<Ty<'tcx>> {

View file

@ -176,6 +176,41 @@ fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) {
euv.walk_fn(decl, body);
}
fn adjust_upvar_borrow_kind_for_consume(&self,
cmt: mc::cmt<'tcx>,
mode: euv::ConsumeMode)
{
debug!("adjust_upvar_borrow_kind_for_consume(cmt={}, mode={:?})",
cmt.repr(self.tcx()), mode);
// we only care about moves
match mode {
euv::Copy => { return; }
euv::Move(_) => { }
}
// watch out for a move of the deref of a borrowed pointer;
// for that to be legal, the upvar would have to be borrowed
// by value instead
let guarantor = cmt.guarantor();
debug!("adjust_upvar_borrow_kind_for_consume: guarantor={}",
guarantor.repr(self.tcx()));
match guarantor.cat {
mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
mc::cat_deref(_, _, mc::Implicit(..)) => {
if let mc::NoteUpvarRef(upvar_id) = cmt.note {
debug!("adjust_upvar_borrow_kind_for_consume: \
setting upvar_id={:?} to by value",
upvar_id);
let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue);
}
}
_ => { }
}
}
/// Indicates that `cmt` is being directly mutated (e.g., assigned
/// to). If cmt contains any by-ref upvars, this implies that
/// those upvars must be borrowed using an `&mut` borow.
@ -319,9 +354,12 @@ impl<'a,'tcx> euv::Delegate<'tcx> for AdjustBorrowKind<'a,'tcx> {
fn consume(&mut self,
_consume_id: ast::NodeId,
_consume_span: Span,
_cmt: mc::cmt<'tcx>,
_mode: euv::ConsumeMode)
{}
cmt: mc::cmt<'tcx>,
mode: euv::ConsumeMode)
{
debug!("consume(cmt={},mode={:?})", cmt.repr(self.tcx()), mode);
self.adjust_upvar_borrow_kind_for_consume(cmt, mode);
}
fn matched_pat(&mut self,
_matched_pat: &ast::Pat,
@ -331,9 +369,12 @@ fn matched_pat(&mut self,
fn consume_pat(&mut self,
_consume_pat: &ast::Pat,
_cmt: mc::cmt<'tcx>,
_mode: euv::ConsumeMode)
{}
cmt: mc::cmt<'tcx>,
mode: euv::ConsumeMode)
{
debug!("consume_pat(cmt={},mode={:?})", cmt.repr(self.tcx()), mode);
self.adjust_upvar_borrow_kind_for_consume(cmt, mode);
}
fn borrow(&mut self,
borrow_id: ast::NodeId,

View file

@ -0,0 +1,24 @@
// Copyright 2015 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.
// Test that a by-ref `FnMut` closure gets an error when it tries to
// consume a value.
fn call<F>(f: F) where F : Fn() {
f();
}
fn main() {
let y = vec!(format!("World"));
call(|| {
y.into_iter();
//~^ ERROR cannot move out of captured outer variable in an `Fn` closure
});
}

View file

@ -0,0 +1,24 @@
// Copyright 2015 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.
// Test that a by-ref `FnMut` closure gets an error when it tries to
// mutate a value.
fn call<F>(f: F) where F : Fn() {
f();
}
fn main() {
let mut counter = 0_u32;
call(|| {
counter += 1;
//~^ ERROR cannot assign to data in a captured outer variable in an `Fn` closure
});
}

View file

@ -0,0 +1,36 @@
// Copyright 2015 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.
// Test that we mutate a counter on the stack only when we expect to.
fn call<F>(f: F) where F : FnOnce() {
f();
}
fn main() {
let y = vec!(format!("Hello"), format!("World"));
let mut counter = 22_u32;
call(|| {
// Move `y`, but do not move `counter`, even though it is read
// by value (note that it is also mutated).
for item in y.into_iter() {
let v = counter;
counter += v;
}
});
assert_eq!(counter, 88);
call(move || {
// this mutates a moved copy, and hence doesn't affect original
counter += 1;
});
assert_eq!(counter, 88);
}

View file

@ -0,0 +1,32 @@
// Copyright 2015 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.
// Test that in a by-ref once closure we move some variables even as
// we capture others by mutable reference.
fn call<F>(f: F) where F : FnOnce() {
f();
}
fn main() {
let mut x = vec!(format!("Hello"));
let y = vec!(format!("World"));
call(|| {
// Here: `x` must be captured with a mutable reference in
// order for us to append on it, and `y` must be captured by
// value.
for item in y.into_iter() {
x.push(item);
}
});
assert_eq!(x.len(), 2);
assert_eq!(&*x[0], "Hello");
assert_eq!(&*x[1], "World");
}