about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBen Blum <bblum@andrew.cmu.edu>2013-06-21 17:58:21 -0400
committerBen Blum <bblum@andrew.cmu.edu>2013-06-29 04:39:37 -0400
commitd4722e53332a854228a2db6a682da5e0017fe73e (patch)
tree8eb3badf05ee4a3b6a55e5cf3d9e2c461b03047e
parent98c169e4e594fda871628edc4b9c82a56072c0df (diff)
downloadrust-d4722e53332a854228a2db6a682da5e0017fe73e.tar.gz
rust-d4722e53332a854228a2db6a682da5e0017fe73e.zip
Trade stack closure copyability for type soundness.
-rw-r--r--src/librustc/middle/kind.rs37
-rw-r--r--src/librustc/middle/ty.rs13
-rw-r--r--src/test/compile-fail/kindck-owned.rs2
3 files changed, 37 insertions, 15 deletions
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index f0c091ac53c..d4b91ed589d 100644
--- a/src/librustc/middle/kind.rs
+++ b/src/librustc/middle/kind.rs
@@ -171,7 +171,7 @@ fn with_appropriate_checker(cx: Context, id: node_id,
         // check that only immutable variables are implicitly copied in
         check_imm_free_var(cx, fv.def, fv.span);
 
-        check_freevar_bounds(cx, fv.span, var_t, bounds);
+        check_freevar_bounds(cx, fv.span, var_t, bounds, None);
     }
 
     fn check_for_box(cx: Context, fv: &freevar_entry, bounds: ty::BuiltinBounds) {
@@ -182,13 +182,18 @@ fn with_appropriate_checker(cx: Context, id: node_id,
         // check that only immutable variables are implicitly copied in
         check_imm_free_var(cx, fv.def, fv.span);
 
-        check_freevar_bounds(cx, fv.span, var_t, bounds);
+        check_freevar_bounds(cx, fv.span, var_t, bounds, None);
     }
 
-    fn check_for_block(cx: Context, fv: &freevar_entry, bounds: ty::BuiltinBounds) {
+    fn check_for_block(cx: Context, fv: &freevar_entry,
+                       bounds: ty::BuiltinBounds, region: ty::Region) {
         let id = ast_util::def_id_of_def(fv.def).node;
         let var_t = ty::node_id_to_type(cx.tcx, id);
-        check_freevar_bounds(cx, fv.span, var_t, bounds);
+        // FIXME(#3569): Figure out whether the implicit borrow is actually
+        // mutable. Currently we assume all upvars are referenced mutably.
+        let implicit_borrowed_type = ty::mk_mut_rptr(cx.tcx, region, var_t);
+        check_freevar_bounds(cx, fv.span, implicit_borrowed_type,
+                             bounds, Some(var_t));
     }
 
     fn check_for_bare(cx: Context, fv: @freevar_entry) {
@@ -205,8 +210,9 @@ fn with_appropriate_checker(cx: Context, id: node_id,
         ty::ty_closure(ty::ClosureTy {sigil: ManagedSigil, bounds: bounds, _}) => {
             b(|cx, fv| check_for_box(cx, fv, bounds))
         }
-        ty::ty_closure(ty::ClosureTy {sigil: BorrowedSigil, bounds: bounds, _}) => {
-            b(|cx, fv| check_for_block(cx, fv, bounds))
+        ty::ty_closure(ty::ClosureTy {sigil: BorrowedSigil, bounds: bounds,
+                                      region: region, _}) => {
+            b(|cx, fv| check_for_block(cx, fv, bounds, region))
         }
         ty::ty_bare_fn(_) => {
             b(check_for_bare)
@@ -366,14 +372,21 @@ pub fn check_typaram_bounds(cx: Context,
 }
 
 pub fn check_freevar_bounds(cx: Context, sp: span, ty: ty::t,
-                            bounds: ty::BuiltinBounds)
+                            bounds: ty::BuiltinBounds, referenced_ty: Option<ty::t>)
 {
     do check_builtin_bounds(cx, ty, bounds) |missing| {
-        cx.tcx.sess.span_err(
-            sp,
-            fmt!("cannot capture variable of type `%s`, which does not fulfill \
-                  `%s`, in a bounded closure",
-                 ty_to_str(cx.tcx, ty), missing.user_string(cx.tcx)));
+        // Will be Some if the freevar is implicitly borrowed (stack closure).
+        // Emit a less mysterious error message in this case.
+        match referenced_ty {
+            Some(rty) => cx.tcx.sess.span_err(sp,
+                fmt!("cannot implicitly borrow variable of type `%s` in a bounded \
+                      stack closure (implicit reference does not fulfill `%s`)",
+                     ty_to_str(cx.tcx, rty), missing.user_string(cx.tcx))),
+            None => cx.tcx.sess.span_err(sp,
+                fmt!("cannot capture variable of type `%s`, which does \
+                      not fulfill `%s`, in a bounded closure",
+                     ty_to_str(cx.tcx, ty), missing.user_string(cx.tcx))),
+        }
         cx.tcx.sess.span_note(
             sp,
             fmt!("this closure's environment must satisfy `%s`",
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 9f9127663af..b8ee1eee26e 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -1828,7 +1828,9 @@ impl TypeContents {
             // Currently all noncopyable existentials are 2nd-class types
             // behind owned pointers. With dynamically-sized types, remove
             // this assertion.
-            assert!(self.intersects(TC_OWNED_POINTER));
+            assert!(self.intersects(TC_OWNED_POINTER) ||
+                    // (...or stack closures without a copy bound.)
+                    self.intersects(TC_BORROWED_POINTER));
         }
         let tc = TC_MANAGED + TC_DTOR + TypeContents::sendable(cx);
         self.intersects(tc)
@@ -2194,7 +2196,14 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
             ast::Once => TC_ONCE_CLOSURE,
             ast::Many => TC_NONE
         };
-        st + rt + ot
+        // Prevent noncopyable types captured in the environment from being copied.
+        let ct = if cty.bounds.contains_elem(BoundCopy) ||
+                    cty.sigil == ast::ManagedSigil {
+            TC_NONE
+        } else {
+            TC_NONCOPY_TRAIT
+        };
+        st + rt + ot + ct
     }
 
     fn trait_contents(store: TraitStore, mutbl: ast::mutability,
diff --git a/src/test/compile-fail/kindck-owned.rs b/src/test/compile-fail/kindck-owned.rs
index 3f859b7dc84..848fd95a560 100644
--- a/src/test/compile-fail/kindck-owned.rs
+++ b/src/test/compile-fail/kindck-owned.rs
@@ -29,6 +29,6 @@ fn main() {
     copy2(boxed);
     let owned: ~fn() = || {};
     copy2(owned);    //~ ERROR does not fulfill `Copy`
-    let borrowed: &fn() = || {};
+    let borrowed: &fn:Copy() = || {};
     copy2(borrowed); //~ ERROR does not fulfill `'static`
 }