diff options
Diffstat (limited to 'src')
7 files changed, 167 insertions, 7 deletions
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 7ef6ba3c190..9457a1a99f6 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -366,6 +366,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { 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); } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 0c4bec48dd4..aa876fcb5f6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -305,7 +305,7 @@ impl<'a, 'tcx> mc::Typer<'tcx> for FnCtxt<'a, '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>> { diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 22e4716f4bd..449220b1c85 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -176,6 +176,41 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { 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 @@ impl<'a,'tcx> euv::Delegate<'tcx> for AdjustBorrowKind<'a,'tcx> { 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, diff --git a/src/test/compile-fail/unboxed-closures-move-upvar-from-non-once-ref-closure.rs b/src/test/compile-fail/unboxed-closures-move-upvar-from-non-once-ref-closure.rs new file mode 100644 index 00000000000..e66610c1496 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-move-upvar-from-non-once-ref-closure.rs @@ -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 + }); +} diff --git a/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs b/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs new file mode 100644 index 00000000000..2345a86595e --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs @@ -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 + }); +} diff --git a/src/test/run-pass/unboxed-closures-counter-not-moved.rs b/src/test/run-pass/unboxed-closures-counter-not-moved.rs new file mode 100644 index 00000000000..e921f0c723e --- /dev/null +++ b/src/test/run-pass/unboxed-closures-counter-not-moved.rs @@ -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); +} diff --git a/src/test/run-pass/unboxed-closures-move-some-upvars-in-by-ref-closure.rs b/src/test/run-pass/unboxed-closures-move-some-upvars-in-by-ref-closure.rs new file mode 100644 index 00000000000..9534ee6fa12 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-move-some-upvars-in-by-ref-closure.rs @@ -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"); +} |
