From be08c3e5146953619ff777aaa422152dfee4ad28 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 May 2013 16:26:43 -0400 Subject: [PATCH] rustc: add rooting, write-guards to slices etc --- src/librustc/middle/trans/_match.rs | 25 +++++-- src/librustc/middle/trans/controlflow.rs | 2 +- src/librustc/middle/trans/datum.rs | 70 ++++++++++++++----- src/librustc/middle/trans/expr.rs | 19 +++-- src/test/run-fail/borrowck-wg-fail-2.rs | 3 + src/test/run-fail/borrowck-wg-fail-3.rs | 3 + src/test/run-fail/borrowck-wg-fail.rs | 3 + ...orrowck-wg-one-mut-one-imm-slice-method.rs | 37 ++++++++++ .../borrowck-wg-one-mut-one-imm-slices.rs | 16 +++++ .../run-fail/borrowck-wg-one-mut-one-imm.rs | 17 +++++ .../run-fail/borrowck-wg-two-array-indices.rs | 17 +++++ .../run-pass/borrowck-wg-two-imm-borrows.rs | 14 ++++ 12 files changed, 194 insertions(+), 32 deletions(-) create mode 100644 src/test/run-fail/borrowck-wg-one-mut-one-imm-slice-method.rs create mode 100644 src/test/run-fail/borrowck-wg-one-mut-one-imm-slices.rs create mode 100644 src/test/run-fail/borrowck-wg-one-mut-one-imm.rs create mode 100644 src/test/run-fail/borrowck-wg-two-array-indices.rs create mode 100644 src/test/run-pass/borrowck-wg-two-imm-borrows.rs diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 80e34ca48142..3b1cdf0ba47f 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -866,7 +866,18 @@ pub fn extract_variant_args(bcx: block, ExtractedBlock { vals: args, bcx: bcx } } +fn match_datum(bcx: block, val: ValueRef, pat_id: ast::node_id) -> Datum { + //! Helper for converting from the ValueRef that we pass around in + //! the match code, which is always by ref, into a Datum. Eventually + //! we should just pass around a Datum and be done with it. + + let ty = node_id_type(bcx, pat_id); + Datum {val: val, ty: ty, mode: datum::ByRef, source: RevokeClean} +} + + pub fn extract_vec_elems(bcx: block, + pat_span: span, pat_id: ast::node_id, elem_count: uint, slice: Option, @@ -874,9 +885,9 @@ pub fn extract_vec_elems(bcx: block, count: ValueRef) -> ExtractedBlock { let _icx = bcx.insn_ctxt("match::extract_vec_elems"); + let vec_datum = match_datum(bcx, val, pat_id); + let (bcx, base, len) = vec_datum.get_vec_base_and_len(bcx, pat_span, pat_id); let vt = tvec::vec_types(bcx, node_id_type(bcx, pat_id)); - let unboxed = load_if_immediate(bcx, val, vt.vec_ty); - let (base, len) = tvec::get_base_and_len(bcx, unboxed, vt.vec_ty); let mut elems = do vec::from_fn(elem_count) |i| { match slice { @@ -1308,10 +1319,14 @@ pub fn compile_submatch(bcx: block, vec::slice(vals, col + 1u, vals.len())); let ccx = *bcx.fcx.ccx; let mut pat_id = 0; + let mut pat_span = dummy_sp(); for vec::each(m) |br| { // Find a real id (we're adding placeholder wildcard patterns, but // each column is guaranteed to have at least one real pattern) - if pat_id == 0 { pat_id = br.pats[col].id; } + if pat_id == 0 { + pat_id = br.pats[col].id; + pat_span = br.pats[col].span; + } } // If we are not matching against an `@T`, we should not be @@ -1579,8 +1594,8 @@ pub fn compile_submatch(bcx: block, vec_len_ge(_, i) => Some(i), _ => None }; - let args = extract_vec_elems(opt_cx, pat_id, n, slice, - val, test_val); + let args = extract_vec_elems(opt_cx, pat_span, pat_id, n, slice, + val, test_val); size = args.vals.len(); unpacked = /*bad*/copy args.vals; opt_cx = args.bcx; diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 60b6cf9e23f7..c8699cc6371b 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -333,7 +333,7 @@ pub fn trans_fail_expr(bcx: block, bcx, expr::trans_to_datum(bcx, arg_expr)); if ty::type_is_str(arg_datum.ty) { - let (lldata, _lllen) = arg_datum.get_base_and_len(bcx); + let (lldata, _) = arg_datum.get_vec_base_and_len_no_root(bcx); return trans_fail_value(bcx, sp_opt, lldata); } else if bcx.unreachable || ty::type_is_bot(arg_datum.ty) { return bcx; diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index 095798ae2127..6ffe504b804f 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -603,6 +603,8 @@ pub impl Datum { } fn perform_write_guard(&self, bcx: block, span: span) -> block { + debug!("perform_write_guard"); + // Create scratch space, but do not root it. let llval = match self.mode { ByValue => self.val, @@ -682,25 +684,10 @@ pub impl Datum { { let ccx = bcx.ccx(); - debug!("try_deref(expr_id=%d, derefs=%?, is_auto=%b, self=%?)", + debug!("try_deref(expr_id=%?, derefs=%?, is_auto=%b, self=%?)", expr_id, derefs, is_auto, self.to_str(bcx.ccx())); - let _indenter = indenter(); - // root the autoderef'd value, if necessary: - // - // (Note: root'd values are always boxes) - let key = root_map_key { id: expr_id, derefs: derefs }; - let bcx = match ccx.maps.root_map.find(&key) { - None => bcx, - Some(&root_info) => self.root(bcx, span, key, root_info) - }; - - // Perform the write guard, if necessary. - // - // (Note: write-guarded values are always boxes) - let bcx = if ccx.maps.write_guard_map.contains(&key) { - self.perform_write_guard(bcx, span) - } else { bcx }; + let bcx = self.root_and_write_guard(bcx, span, expr_id, derefs); match ty::get(self.ty).sty { ty::ty_box(_) | ty::ty_uniq(_) => { @@ -854,8 +841,53 @@ pub impl Datum { DatumBlock { bcx: bcx, datum: datum } } - fn get_base_and_len(&self, bcx: block) -> (ValueRef, ValueRef) { - tvec::get_base_and_len(bcx, self.to_appropriate_llval(bcx), self.ty) + fn root_and_write_guard(&self, + mut bcx: block, + span: span, + expr_id: ast::node_id, + derefs: uint) -> block { + let key = root_map_key { id: expr_id, derefs: derefs }; + debug!("root_and_write_guard(key=%?)", key); + + // root the autoderef'd value, if necessary: + // + // (Note: root'd values are always boxes) + let ccx = bcx.ccx(); + bcx = match ccx.maps.root_map.find(&key) { + None => bcx, + Some(&root_info) => self.root(bcx, span, key, root_info) + }; + + // Perform the write guard, if necessary. + // + // (Note: write-guarded values are always boxes) + if ccx.maps.write_guard_map.contains(&key) { + self.perform_write_guard(bcx, span) + } else { + bcx + } + } + + fn get_vec_base_and_len(&self, + mut bcx: block, + span: span, + expr_id: ast::node_id) + -> (block, ValueRef, ValueRef) { + //! Converts a vector into the slice pair. Performs rooting + //! and write guards checks. + + // only imp't for @[] and @str, but harmless + bcx = self.root_and_write_guard(bcx, span, expr_id, 0); + let (base, len) = self.get_vec_base_and_len_no_root(bcx); + (bcx, base, len) + } + + fn get_vec_base_and_len_no_root(&self, bcx: block) -> (ValueRef, ValueRef) { + //! Converts a vector into the slice pair. Des not root + //! nor perform write guard checks. + + let llval = self.to_appropriate_llval(bcx); + tvec::get_base_and_len(bcx, llval, self.ty) } fn to_result(&self, bcx: block) -> common::Result { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 1a9824dcfe8a..b8cdfeb796db 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -218,10 +218,10 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { unpack_datum!(bcx, auto_ref(bcx, datum)) } Some(AutoBorrowVec(*)) => { - unpack_datum!(bcx, auto_slice(bcx, datum)) + unpack_datum!(bcx, auto_slice(bcx, expr, datum)) } Some(AutoBorrowVecRef(*)) => { - unpack_datum!(bcx, auto_slice_and_ref(bcx, datum)) + unpack_datum!(bcx, auto_slice_and_ref(bcx, expr, datum)) } Some(AutoBorrowFn(*)) => { // currently, all closure types are @@ -241,7 +241,7 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { DatumBlock {bcx: bcx, datum: datum.to_rptr(bcx)} } - fn auto_slice(bcx: block, datum: Datum) -> DatumBlock { + fn auto_slice(bcx: block, expr: @ast::expr, datum: Datum) -> DatumBlock { // This is not the most efficient thing possible; since slices // are two words it'd be better if this were compiled in // 'dest' mode, but I can't find a nice way to structure the @@ -250,7 +250,9 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { let tcx = bcx.tcx(); let unit_ty = ty::sequence_element_type(tcx, datum.ty); - let (base, len) = datum.get_base_and_len(bcx); + // NOTE prob need to distinguish "auto-slice" from explicit index? + let (bcx, base, len) = + datum.get_vec_base_and_len(bcx, expr.span, expr.id); // this type may have a different region/mutability than the // real one, but it will have the same runtime representation @@ -283,8 +285,10 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { DatumBlock {bcx: bcx, datum: scratch} } - fn auto_slice_and_ref(bcx: block, datum: Datum) -> DatumBlock { - let DatumBlock { bcx, datum } = auto_slice(bcx, datum); + fn auto_slice_and_ref(bcx: block, + expr: @ast::expr, + datum: Datum) -> DatumBlock { + let DatumBlock { bcx, datum } = auto_slice(bcx, expr, datum); auto_ref(bcx, datum) } } @@ -903,7 +907,8 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock { let scaled_ix = Mul(bcx, ix_val, vt.llunit_size); base::maybe_name_value(bcx.ccx(), scaled_ix, ~"scaled_ix"); - let mut (base, len) = base_datum.get_base_and_len(bcx); + let mut (bcx, base, len) = + base_datum.get_vec_base_and_len(bcx, index_expr.span, index_expr.id); if ty::type_is_str(base_ty) { // acccount for null terminator in the case of string diff --git a/src/test/run-fail/borrowck-wg-fail-2.rs b/src/test/run-fail/borrowck-wg-fail-2.rs index 121ec9c79216..59a5fecd3400 100644 --- a/src/test/run-fail/borrowck-wg-fail-2.rs +++ b/src/test/run-fail/borrowck-wg-fail-2.rs @@ -1,5 +1,8 @@ // error-pattern:borrowed +// Test that write guards trigger when there is a write to a field +// of a frozen structure. + struct S { x: int } diff --git a/src/test/run-fail/borrowck-wg-fail-3.rs b/src/test/run-fail/borrowck-wg-fail-3.rs index 2b95cf3fe5fa..ebff553aafba 100644 --- a/src/test/run-fail/borrowck-wg-fail-3.rs +++ b/src/test/run-fail/borrowck-wg-fail-3.rs @@ -1,5 +1,8 @@ // error-pattern:borrowed +// Test that write guards trigger when there is a write to a directly +// frozen @mut box. + fn main() { let x = @mut 3; let y: &mut int = x; diff --git a/src/test/run-fail/borrowck-wg-fail.rs b/src/test/run-fail/borrowck-wg-fail.rs index fd2d36b895ad..939d802c21ca 100644 --- a/src/test/run-fail/borrowck-wg-fail.rs +++ b/src/test/run-fail/borrowck-wg-fail.rs @@ -1,5 +1,8 @@ // error-pattern:borrowed +// Test that write guards trigger when mut box is frozen +// as part of argument coercion. + fn f(_x: &int, y: @mut int) { *y = 2; } diff --git a/src/test/run-fail/borrowck-wg-one-mut-one-imm-slice-method.rs b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slice-method.rs new file mode 100644 index 000000000000..91df90f8b3ac --- /dev/null +++ b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slice-method.rs @@ -0,0 +1,37 @@ +// error-pattern:borrowed + +// Test that write guards trigger when there is a coercion to +// a slice on the receiver of a method. + +trait MyMutSlice { + fn my_mut_slice(self) -> Self; +} + +impl<'self, T> MyMutSlice for &'self mut [T] { + fn my_mut_slice(self) -> &'self mut [T] { + self + } +} + +trait MySlice { + fn my_slice(self) -> Self; +} + +impl<'self, T> MySlice for &'self [T] { + fn my_slice(self) -> &'self [T] { + self + } +} + +fn add(x:&mut [int], y:&[int]) +{ + x[0] = x[0] + y[0]; +} + +pub fn main() +{ + let z = @mut [1,2,3]; + let z2 = z; + add(z.my_mut_slice(), z2.my_slice()); + print(fmt!("%d\n", z[0])); +} diff --git a/src/test/run-fail/borrowck-wg-one-mut-one-imm-slices.rs b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slices.rs new file mode 100644 index 000000000000..bae693ce4eae --- /dev/null +++ b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slices.rs @@ -0,0 +1,16 @@ +// error-pattern:borrowed + +// Test that write guards trigger when arguments are coerced to slices. + +fn add(x:&mut [int], y:&[int]) +{ + x[0] = x[0] + y[0]; +} + +pub fn main() +{ + let z = @mut [1,2,3]; + let z2 = z; + add(z, z2); + print(fmt!("%d\n", z[0])); +} diff --git a/src/test/run-fail/borrowck-wg-one-mut-one-imm.rs b/src/test/run-fail/borrowck-wg-one-mut-one-imm.rs new file mode 100644 index 000000000000..9e2a02b32dfe --- /dev/null +++ b/src/test/run-fail/borrowck-wg-one-mut-one-imm.rs @@ -0,0 +1,17 @@ +// error-pattern:borrowed + +// Test that write guards trigger when we are indexing into +// an @mut vector. + +fn add(x:&mut int, y:&int) +{ + *x = *x + *y; +} + +pub fn main() +{ + let z = @mut [1,2,3]; + let z2 = z; + add(&mut z[0], &z2[0]); + print(fmt!("%d\n", z[0])); +} diff --git a/src/test/run-fail/borrowck-wg-two-array-indices.rs b/src/test/run-fail/borrowck-wg-two-array-indices.rs new file mode 100644 index 000000000000..ad6844887602 --- /dev/null +++ b/src/test/run-fail/borrowck-wg-two-array-indices.rs @@ -0,0 +1,17 @@ +// error-pattern:borrowed + +// Test that arguments trigger when there are *two mutable* borrows +// of indices. + +fn add(x:&mut int, y:&mut int) +{ + *x = *x + *y; +} + +pub fn main() +{ + let z = @mut [1,2,3]; + let z2 = z; + add(&mut z[0], &mut z2[0]); + print(fmt!("%d\n", z[0])); +} diff --git a/src/test/run-pass/borrowck-wg-two-imm-borrows.rs b/src/test/run-pass/borrowck-wg-two-imm-borrows.rs new file mode 100644 index 000000000000..20f824e969a4 --- /dev/null +++ b/src/test/run-pass/borrowck-wg-two-imm-borrows.rs @@ -0,0 +1,14 @@ +// Test that we can borrow the same @mut box twice, so long as both are imm. + +fn add(x:&int, y:&int) +{ + *x + *y; +} + +pub fn main() +{ + let z = @mut [1,2,3]; + let z2 = z; + add(&z[0], &z2[0]); + print(fmt!("%d\n", z[0])); +}