about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-05-31 14:02:39 -0700
committerNiko Matsakis <niko@alum.mit.edu>2012-05-31 15:07:09 -0700
commitc2ce2741a773b94d3a8f7293cb598322bc61f89d (patch)
tree8736739f4af685ecab254939ea9e288dbaff82df
parent0470abe1d290ef28fa72693b70f955328656e471 (diff)
downloadrust-c2ce2741a773b94d3a8f7293cb598322bc61f89d.tar.gz
rust-c2ce2741a773b94d3a8f7293cb598322bc61f89d.zip
allow mutable vectors and so forth to be used as immutable slices
-rw-r--r--src/rustc/middle/astencode.rs21
-rw-r--r--src/rustc/middle/borrowck.rs6
-rw-r--r--src/rustc/middle/ty.rs9
-rw-r--r--src/rustc/middle/typeck/infer.rs110
-rw-r--r--src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs17
-rw-r--r--src/test/run-pass/borrowck-mut-vec-as-imm-slice.rs16
6 files changed, 121 insertions, 58 deletions
diff --git a/src/rustc/middle/astencode.rs b/src/rustc/middle/astencode.rs
index 4d412447797..2fd59cb2166 100644
--- a/src/rustc/middle/astencode.rs
+++ b/src/rustc/middle/astencode.rs
@@ -563,6 +563,17 @@ impl of tr for method_origin {
 }
 
 // ______________________________________________________________________
+// Encoding and decoding of borrow
+
+impl helper for ebml::ebml_deserializer {
+    fn read_borrow(xcx: extended_decode_ctxt) -> ty::borrow {
+        let borrow = ty::deserialize_borrow(self);
+        {scope_id: xcx.tr_id(borrow.scope_id),
+         mutbl: borrow.mutbl}
+    }
+}
+
+// ______________________________________________________________________
 // Encoding and decoding vtable_res
 
 fn encode_vtable_res(ecx: @e::encode_ctxt,
@@ -866,10 +877,12 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
         }
     }
 
-    option::iter(tcx.borrowings.find(id)) {|s|
+    option::iter(tcx.borrowings.find(id)) {|borrow|
         ebml_w.tag(c::tag_table_borrowings) {||
             ebml_w.id(id);
-            ebml_w.wr_tagged_i64(c::tag_table_val as uint, s as i64);
+            ebml_w.tag(c::tag_table_val) {||
+                ty::serialize_borrow(ebml_w, borrow)
+            }
         }
     }
 }
@@ -979,8 +992,8 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
                 dcx.maps.vtable_map.insert(id,
                                            val_dsr.read_vtable_res(xcx));
             } else if tag == (c::tag_table_borrowings as uint) {
-                let scope_id = ebml::doc_as_i64(val_doc) as int;
-                dcx.tcx.borrowings.insert(id, scope_id);
+                let borrow = val_dsr.read_borrow(xcx);
+                dcx.tcx.borrowings.insert(id, borrow);
             } else {
                 xcx.dcx.tcx.sess.bug(
                     #fmt["unknown tag found in side tables: %x", tag]);
diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs
index 01363393976..bc7cd7a55c3 100644
--- a/src/rustc/middle/borrowck.rs
+++ b/src/rustc/middle/borrowck.rs
@@ -190,10 +190,10 @@ fn req_loans_in_expr(ex: @ast::expr,
     let tcx = bccx.tcx;
 
     // If this expression is borrowed, have to ensure it remains valid:
-    for tcx.borrowings.find(ex.id).each { |scope_id|
+    for tcx.borrowings.find(ex.id).each { |borrow|
         let cmt = self.bccx.cat_borrow_of_expr(ex);
-        let scope_r = ty::re_scope(scope_id);
-        self.guarantee_valid(cmt, m_const, scope_r);
+        let scope_r = ty::re_scope(borrow.scope_id);
+        self.guarantee_valid(cmt, borrow.mutbl, scope_r);
     }
 
     // Special checks for various kinds of expressions:
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index 5fced36e408..04ea9fe01bb 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -158,6 +158,7 @@ export mach_sty;
 export ty_sort_str;
 export normalize_ty;
 export to_str;
+export borrow, serialize_borrow, deserialize_borrow;
 
 // Data types
 
@@ -204,6 +205,12 @@ enum ast_ty_to_ty_cache_entry {
     atttce_resolved(t)  /* resolved to a type, irrespective of region */
 }
 
+#[auto_serialize]
+type borrow = {
+    scope_id: ast::node_id,
+    mutbl: ast::mutability
+};
+
 type ctxt =
     @{diag: syntax::diagnostic::span_handler,
       interner: hashmap<intern_key, t_box>,
@@ -239,7 +246,7 @@ type ctxt =
       ty_param_bounds: hashmap<ast::node_id, param_bounds>,
       inferred_modes: hashmap<ast::node_id, ast::mode>,
       // maps the id of borrowed expr to scope of borrowed ptr
-      borrowings: hashmap<ast::node_id, ast::node_id>,
+      borrowings: hashmap<ast::node_id, borrow>,
       normalized_cache: hashmap<t, t>};
 
 enum tbox_flag {
diff --git a/src/rustc/middle/typeck/infer.rs b/src/rustc/middle/typeck/infer.rs
index 2e2d4e629ce..38466400e57 100644
--- a/src/rustc/middle/typeck/infer.rs
+++ b/src/rustc/middle/typeck/infer.rs
@@ -157,6 +157,7 @@ import check::regionmanip::{replace_bound_regions_in_fn_ty};
 import driver::session::session;
 import util::common::{indent, indenter};
 import ast::{unsafe_fn, impure_fn, pure_fn, crust_fn};
+import ast::{m_const, m_imm, m_mutbl};
 
 export infer_ctxt;
 export new_infer_ctxt;
@@ -961,24 +962,27 @@ impl methods for resolve_state {
 // we just fall back to requiring that a <: b.
 //
 // Assuming we have a bound from both sides, we will then examine
-// these bounds and see if they have the form (@MT_a, &rb.MT_b)
-// (resp. ~MT_a).  If they do not, we fall back to subtyping.
+// these bounds and see if they have the form (@M_a T_a, &rb.M_b T_b)
+// (resp. ~M_a T_a, [M_a T_a], etc).  If they do not, we fall back to
+// subtyping.
 //
 // If they *do*, then we know that the two types could never be
-// subtypes of one another.  We will then construct a type @MT_b and
-// ensure that type a is a subtype of that.  This allows for the
+// subtypes of one another.  We will then construct a type @const T_b
+// and ensure that type a is a subtype of that.  This allows for the
 // possibility of assigning from a type like (say) @[mut T1] to a type
-// &[const T2] where T1 <: T2.  Basically we would require that @[mut
-// T1] <: @[const T2].  Next we require that the region for the
-// enclosing scope be a superregion of the region r.  These two checks
-// together guarantee that the type A would be a subtype of the type B
-// if the @ were converted to a region r.
+// &[T2] where T1 <: T2.  This might seem surprising, since the `@`
+// points at mutable memory but the `&` points at immutable memory.
+// This would in fact be unsound, except for the borrowck, which comes
+// later and guarantees that such mutability conversions are safe.
+// See borrowck for more details.  Next we require that the region for
+// the enclosing scope be a superregion of the region r.
 //
-// You might wonder why we don't just make the type &e.MT_a where e is
-// the enclosing region and check that &e.MT_a <: B.  The reason is
-// that the type @MT_a is (generally) just a *lower-bound*, so this
-// would be imposing @MT_a also as the upper-bound on type A.  But
-// this upper-bound might be stricter than what is truly needed.
+// You might wonder why we don't make the type &e.const T_a where e is
+// the enclosing region and check that &e.const T_a <: B.  The reason
+// is that the type of A is (generally) just a *lower-bound*, so this
+// would be imposing that lower-bound also as the upper-bound on type
+// A.  But this upper-bound might be stricter than what is truly
+// needed.
 
 impl assignment for infer_ctxt {
     fn assign_tys(anmnt: assignment, a: ty::t, b: ty::t) -> ures {
@@ -1051,35 +1055,39 @@ impl assignment for infer_ctxt {
           (some(a_bnd), some(b_bnd)) {
             alt (ty::get(a_bnd).struct, ty::get(b_bnd).struct) {
               (ty::ty_box(mt_a), ty::ty_rptr(r_b, mt_b)) {
-                let nr_b = ty::mk_box(self.tcx, mt_b);
-                self.crosspollinate(anmnt, a, nr_b, r_b)
+                let nr_b = ty::mk_box(self.tcx, {ty: mt_b.ty,
+                                                 mutbl: m_const});
+                self.crosspollinate(anmnt, a, nr_b, mt_b.mutbl, r_b)
               }
               (ty::ty_uniq(mt_a), ty::ty_rptr(r_b, mt_b)) {
-                let nr_b = ty::mk_uniq(self.tcx, mt_b);
-                self.crosspollinate(anmnt, a, nr_b, r_b)
+                let nr_b = ty::mk_uniq(self.tcx, {ty: mt_b.ty,
+                                                  mutbl: m_const});
+                self.crosspollinate(anmnt, a, nr_b, mt_b.mutbl, r_b)
               }
               (ty::ty_estr(vs_a),
                ty::ty_estr(ty::vstore_slice(r_b)))
               if is_borrowable(vs_a) {
                 let nr_b = ty::mk_estr(self.tcx, vs_a);
-                self.crosspollinate(anmnt, a, nr_b, r_b)
+                self.crosspollinate(anmnt, a, nr_b, m_imm, r_b)
               }
               (ty::ty_str,
                ty::ty_estr(ty::vstore_slice(r_b))) {
                 let nr_b = ty::mk_str(self.tcx);
-                self.crosspollinate(anmnt, a, nr_b, r_b)
+                self.crosspollinate(anmnt, a, nr_b, m_imm, r_b)
               }
 
               (ty::ty_evec(mt_a, vs_a),
                ty::ty_evec(mt_b, ty::vstore_slice(r_b)))
               if is_borrowable(vs_a) {
-                let nr_b = ty::mk_evec(self.tcx, mt_b, vs_a);
-                self.crosspollinate(anmnt, a, nr_b, r_b)
+                let nr_b = ty::mk_evec(self.tcx, {ty: mt_b.ty,
+                                                  mutbl: m_const}, vs_a);
+                self.crosspollinate(anmnt, a, nr_b, mt_b.mutbl, r_b)
               }
               (ty::ty_vec(mt_a),
                ty::ty_evec(mt_b, ty::vstore_slice(r_b))) {
-                let nr_b = ty::mk_vec(self.tcx, mt_b);
-                self.crosspollinate(anmnt, a, nr_b, r_b)
+                let nr_b = ty::mk_vec(self.tcx, {ty: mt_b.ty,
+                                                 mutbl: m_const});
+                self.crosspollinate(anmnt, a, nr_b, mt_b.mutbl, r_b)
               }
               _ {
                 self.sub_tys(a, b)
@@ -1093,9 +1101,10 @@ impl assignment for infer_ctxt {
     }
 
     fn crosspollinate(anmnt: assignment,
-                     a: ty::t,
-                     nr_b: ty::t,
-                     r_b: ty::region) -> ures {
+                      a: ty::t,
+                      nr_b: ty::t,
+                      m: ast::mutability,
+                      r_b: ty::region) -> ures {
 
         #debug["crosspollinate(anmnt=%?, a=%s, nr_b=%s, r_b=%s)",
                anmnt, a.to_str(self), nr_b.to_str(self),
@@ -1109,8 +1118,9 @@ impl assignment for infer_ctxt {
                     // if successful, add an entry indicating that
                     // borrowing occurred
                     #debug["borrowing expression #%?", anmnt];
-                    self.tcx.borrowings.insert(anmnt.expr_id,
-                                               anmnt.borrow_scope);
+                    let borrow = {scope_id: anmnt.borrow_scope,
+                                  mutbl: m};
+                    self.tcx.borrowings.insert(anmnt.expr_id, borrow);
                     uok()
                 }
             }
@@ -1561,17 +1571,17 @@ impl of combine for sub {
     fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt> {
         #debug("mts(%s <: %s)", a.to_str(*self), b.to_str(*self));
 
-        if a.mutbl != b.mutbl && b.mutbl != ast::m_const {
+        if a.mutbl != b.mutbl && b.mutbl != m_const {
             ret err(ty::terr_mutability);
         }
 
         alt b.mutbl {
-          ast::m_mutbl {
+          m_mutbl {
             // If supertype is mut, subtype must match exactly
             // (i.e., invariant if mut):
             self.infcx().eq_tys(a.ty, b.ty).then {|| ok(a) }
           }
-          ast::m_imm | ast::m_const {
+          m_imm | m_const {
             // Otherwise we can be covariant:
             self.tys(a.ty, b.ty).chain {|_t| ok(a) }
           }
@@ -1710,24 +1720,24 @@ impl of combine for lub {
         let m = if a.mutbl == b.mutbl {
             a.mutbl
         } else {
-            ast::m_const
+            m_const
         };
 
         alt m {
-          ast::m_imm | ast::m_const {
+          m_imm | m_const {
             self.tys(a.ty, b.ty).chain {|t|
                 ok({ty: t, mutbl: m})
             }
           }
 
-          ast::m_mutbl {
+          m_mutbl {
             self.infcx().try {||
                 self.infcx().eq_tys(a.ty, b.ty).then {||
                     ok({ty: a.ty, mutbl: m})
                 }
             }.chain_err {|_e|
                 self.tys(a.ty, b.ty).chain {|t|
-                    ok({ty: t, mutbl: ast::m_const})
+                    ok({ty: t, mutbl: m_const})
                 }
             }
           }
@@ -1893,43 +1903,43 @@ impl of combine for glb {
         alt (a.mutbl, b.mutbl) {
           // If one side or both is mut, then the GLB must use
           // the precise type from the mut side.
-          (ast::m_mutbl, ast::m_const) {
+          (m_mutbl, m_const) {
             sub(*self).tys(a.ty, b.ty).chain {|_t|
-                ok({ty: a.ty, mutbl: ast::m_mutbl})
+                ok({ty: a.ty, mutbl: m_mutbl})
             }
           }
-          (ast::m_const, ast::m_mutbl) {
+          (m_const, m_mutbl) {
             sub(*self).tys(b.ty, a.ty).chain {|_t|
-                ok({ty: b.ty, mutbl: ast::m_mutbl})
+                ok({ty: b.ty, mutbl: m_mutbl})
             }
           }
-          (ast::m_mutbl, ast::m_mutbl) {
+          (m_mutbl, m_mutbl) {
             self.infcx().eq_tys(a.ty, b.ty).then {||
-                ok({ty: a.ty, mutbl: ast::m_mutbl})
+                ok({ty: a.ty, mutbl: m_mutbl})
             }
           }
 
           // If one side or both is immutable, we can use the GLB of
           // both sides but mutbl must be `m_imm`.
-          (ast::m_imm, ast::m_const) |
-          (ast::m_const, ast::m_imm) |
-          (ast::m_imm, ast::m_imm) {
+          (m_imm, m_const) |
+          (m_const, m_imm) |
+          (m_imm, m_imm) {
             self.tys(a.ty, b.ty).chain {|t|
-                ok({ty: t, mutbl: ast::m_imm})
+                ok({ty: t, mutbl: m_imm})
             }
           }
 
           // If both sides are const, then we can use GLB of both
           // sides and mutbl of only `m_const`.
-          (ast::m_const, ast::m_const) {
+          (m_const, m_const) {
             self.tys(a.ty, b.ty).chain {|t|
-                ok({ty: t, mutbl: ast::m_const})
+                ok({ty: t, mutbl: m_const})
             }
           }
 
           // There is no mutual subtype of these combinations.
-          (ast::m_mutbl, ast::m_imm) |
-          (ast::m_imm, ast::m_mutbl) {
+          (m_mutbl, m_imm) |
+          (m_imm, m_mutbl) {
               err(ty::terr_mutability)
           }
         }
diff --git a/src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs b/src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs
new file mode 100644
index 00000000000..803f30f8e33
--- /dev/null
+++ b/src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs
@@ -0,0 +1,17 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
+fn want_slice(v: [int]/&) -> int {
+    let mut sum = 0;
+    for vec::each(v) { |i| sum += i; }
+    ret sum;
+}
+
+fn has_mut_vec(+v: @[mut int]) -> int {
+    want_slice(*v) //! ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
+        //!^ NOTE impure due to access to impure function
+}
+
+fn main() {
+    assert has_mut_vec(@[mut 1, 2, 3]) == 6;
+}
\ No newline at end of file
diff --git a/src/test/run-pass/borrowck-mut-vec-as-imm-slice.rs b/src/test/run-pass/borrowck-mut-vec-as-imm-slice.rs
new file mode 100644
index 00000000000..e6e4c36db35
--- /dev/null
+++ b/src/test/run-pass/borrowck-mut-vec-as-imm-slice.rs
@@ -0,0 +1,16 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
+fn want_slice(v: [int]/&) -> int {
+    let mut sum = 0;
+    for vec::each(v) { |i| sum += i; }
+    ret sum;
+}
+
+fn has_mut_vec(+v: [mut int]) -> int {
+    want_slice(v)
+}
+
+fn main() {
+    assert has_mut_vec([mut 1, 2, 3]) == 6;
+}
\ No newline at end of file