diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2012-05-31 14:02:39 -0700 |
|---|---|---|
| committer | Niko Matsakis <niko@alum.mit.edu> | 2012-05-31 15:07:09 -0700 |
| commit | c2ce2741a773b94d3a8f7293cb598322bc61f89d (patch) | |
| tree | 8736739f4af685ecab254939ea9e288dbaff82df | |
| parent | 0470abe1d290ef28fa72693b70f955328656e471 (diff) | |
| download | rust-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.rs | 21 | ||||
| -rw-r--r-- | src/rustc/middle/borrowck.rs | 6 | ||||
| -rw-r--r-- | src/rustc/middle/ty.rs | 9 | ||||
| -rw-r--r-- | src/rustc/middle/typeck/infer.rs | 110 | ||||
| -rw-r--r-- | src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs | 17 | ||||
| -rw-r--r-- | src/test/run-pass/borrowck-mut-vec-as-imm-slice.rs | 16 |
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 |
