about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/middle/expr_use_visitor.rs3
-rw-r--r--src/librustc_typeck/check/mod.rs2
-rw-r--r--src/librustc_typeck/check/upvar.rs53
-rw-r--r--src/test/compile-fail/unboxed-closures-move-upvar-from-non-once-ref-closure.rs24
-rw-r--r--src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs24
-rw-r--r--src/test/run-pass/unboxed-closures-counter-not-moved.rs36
-rw-r--r--src/test/run-pass/unboxed-closures-move-some-upvars-in-by-ref-closure.rs32
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");
+}