From ab2534974caf39e69b401442ff1a9077b94c46c1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 8 Feb 2013 22:21:45 -0800 Subject: [PATCH] Adjust borrow checker algorithm to address #4856 unsoundness, and then adjust code to match. rs=unsound (will review post-landing) --- src/libcore/hashmap.rs | 8 +- src/libcore/private.rs | 28 +-- src/libcore/str.rs | 8 +- src/libcore/vec.rs | 17 +- src/librustc/middle/borrowck/check_loans.rs | 62 +++--- src/librustc/middle/borrowck/gather_loans.rs | 68 +++--- src/librustc/middle/borrowck/loan.rs | 192 +++++++---------- src/librustc/middle/borrowck/mod.rs | 47 ++++- src/librustc/middle/borrowck/preserve.rs | 15 +- src/librustc/middle/mem_categorization.rs | 195 ++++++++++++------ src/librustc/middle/trans/reflect.rs | 168 ++++++++------- src/librustc/middle/ty.rs | 3 - src/librustc/middle/typeck/infer/lattice.rs | 9 +- .../middle/typeck/infer/region_inference.rs | 112 +++++----- src/librustc/middle/typeck/infer/unify.rs | 90 ++++---- src/libstd/arc.rs | 85 ++++---- src/libstd/priority_queue.rs | 6 +- src/libstd/smallintmap.rs | 9 +- src/libstd/treemap.rs | 62 +++--- .../borrowck-borrow-from-owned-ptr.rs | 124 +++++++++++ .../borrowck-borrow-from-stack-variable.rs | 124 +++++++++++ .../compile-fail/borrowck-confuse-region.rs | 26 --- .../borrowck-imm-field-mut-base.rs | 30 --- ...base.rs => borrowck-insert-during-each.rs} | 27 ++- .../borrowck-reborrow-from-mut.rs | 106 ++++++++++ 25 files changed, 1046 insertions(+), 575 deletions(-) create mode 100644 src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs create mode 100644 src/test/compile-fail/borrowck-borrow-from-stack-variable.rs delete mode 100644 src/test/compile-fail/borrowck-confuse-region.rs delete mode 100644 src/test/compile-fail/borrowck-imm-field-mut-base.rs rename src/test/compile-fail/{borrowck-imm-field-imm-base.rs => borrowck-insert-during-each.rs} (57%) create mode 100644 src/test/compile-fail/borrowck-reborrow-from-mut.rs diff --git a/src/libcore/hashmap.rs b/src/libcore/hashmap.rs index 70358bab468..f8793f7e2ae 100644 --- a/src/libcore/hashmap.rs +++ b/src/libcore/hashmap.rs @@ -383,7 +383,9 @@ pub mod linear { }, }; - self.value_for_bucket(idx) + unsafe { // FIXME(#4903)---requires flow-sensitive borrow checker + ::cast::transmute_region(self.value_for_bucket(idx)) + } } /// Return the value corresponding to the key in the map, or create, @@ -412,7 +414,9 @@ pub mod linear { }, }; - self.value_for_bucket(idx) + unsafe { // FIXME(#4903)---requires flow-sensitive borrow checker + ::cast::transmute_region(self.value_for_bucket(idx)) + } } fn consume(&mut self, f: fn(K, V)) { diff --git a/src/libcore/private.rs b/src/libcore/private.rs index 6864572adff..038f61350b2 100644 --- a/src/libcore/private.rs +++ b/src/libcore/private.rs @@ -256,15 +256,15 @@ pub unsafe fn shared_mutable_state(data: T) -> } #[inline(always)] -pub unsafe fn get_shared_mutable_state(rc: &a/SharedMutableState) - -> &a/mut T { +pub unsafe fn get_shared_mutable_state( + rc: *SharedMutableState) -> *mut T +{ unsafe { let ptr: ~ArcData = cast::reinterpret_cast(&(*rc).data); assert ptr.count > 0; - // Cast us back into the correct region - let r = cast::transmute_region(option::get_ref(&ptr.data)); + let r = cast::transmute(option::get_ref(&ptr.data)); cast::forget(move ptr); - return cast::transmute_mut(r); + return r; } } #[inline(always)] @@ -376,15 +376,17 @@ impl Exclusive { // the exclusive. Supporting that is a work in progress. #[inline(always)] unsafe fn with(f: fn(x: &mut T) -> U) -> U { - let rec = unsafe { get_shared_mutable_state(&self.x) }; - do rec.lock.lock { - if rec.failed { - die!(~"Poisoned exclusive - another task failed inside!"); + unsafe { + let rec = get_shared_mutable_state(&self.x); + do (*rec).lock.lock { + if (*rec).failed { + die!(~"Poisoned exclusive - another task failed inside!"); + } + (*rec).failed = true; + let result = f(&mut (*rec).data); + (*rec).failed = false; + move result } - rec.failed = true; - let result = f(&mut rec.data); - rec.failed = false; - move result } } diff --git a/src/libcore/str.rs b/src/libcore/str.rs index 47e68401485..6665ab6c6f7 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -2071,17 +2071,19 @@ pub mod raw { /// Appends a byte to a string. (Not UTF-8 safe). pub unsafe fn push_byte(s: &mut ~str, b: u8) { - reserve_at_least(&mut *s, s.len() + 1); + let new_len = s.len() + 1; + reserve_at_least(&mut *s, new_len); do as_buf(*s) |buf, len| { let buf: *mut u8 = ::cast::reinterpret_cast(&buf); *ptr::mut_offset(buf, len) = b; } - set_len(&mut *s, s.len() + 1); + set_len(&mut *s, new_len); } /// Appends a vector of bytes to a string. (Not UTF-8 safe). unsafe fn push_bytes(s: &mut ~str, bytes: &[u8]) { - reserve_at_least(&mut *s, s.len() + bytes.len()); + let new_len = s.len() + bytes.len(); + reserve_at_least(&mut *s, new_len); for vec::each(bytes) |byte| { push_byte(&mut *s, *byte); } } diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 9ad5d9f32da..16cad87c060 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -623,13 +623,15 @@ unsafe fn push_fast(v: &mut ~[T], initval: T) { #[inline(never)] fn push_slow(v: &mut ~[T], initval: T) { - reserve_at_least(&mut *v, v.len() + 1u); + let new_len = v.len() + 1; + reserve_at_least(&mut *v, new_len); unsafe { push_fast(v, initval) } } #[inline(always)] pub fn push_all(v: &mut ~[T], rhs: &[const T]) { - reserve(&mut *v, v.len() + rhs.len()); + let new_len = v.len() + rhs.len(); + reserve(&mut *v, new_len); for uint::range(0u, rhs.len()) |i| { push(&mut *v, unsafe { raw::get(rhs, i) }) @@ -638,7 +640,8 @@ pub fn push_all(v: &mut ~[T], rhs: &[const T]) { #[inline(always)] pub fn push_all_move(v: &mut ~[T], mut rhs: ~[T]) { - reserve(&mut *v, v.len() + rhs.len()); + let new_len = v.len() + rhs.len(); + reserve(&mut *v, new_len); unsafe { do as_mut_buf(rhs) |p, len| { for uint::range(0, len) |i| { @@ -663,9 +666,9 @@ pub fn truncate(v: &mut ~[T], newlen: uint) { let mut dropped = rusti::init(); dropped <-> *ptr::mut_offset(p, i); } - raw::set_len(&mut *v, newlen); } } + unsafe { raw::set_len(&mut *v, newlen); } } /** @@ -740,7 +743,8 @@ pub pure fn append_mut(lhs: ~[mut T], rhs: &[const T]) -> ~[mut T] { * * initval - The value for the new elements */ pub fn grow(v: &mut ~[T], n: uint, initval: &T) { - reserve_at_least(&mut *v, v.len() + n); + let new_len = v.len() + n; + reserve_at_least(&mut *v, new_len); let mut i: uint = 0u; while i < n { @@ -763,7 +767,8 @@ pub fn grow(v: &mut ~[T], n: uint, initval: &T) { * value */ pub fn grow_fn(v: &mut ~[T], n: uint, op: iter::InitOp) { - reserve_at_least(&mut *v, v.len() + n); + let new_len = v.len() + n; + reserve_at_least(&mut *v, new_len); let mut i: uint = 0u; while i < n { v.push(op(i)); diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 251fec68486..b6b94faa3db 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -305,22 +305,31 @@ impl CheckLoanCtxt { return; } - match (old_loan.mutbl, new_loan.mutbl) { - (m_const, _) | (_, m_const) | (m_imm, m_imm) => { - /*ok*/ + match (old_loan.kind, new_loan.kind) { + (PartialFreeze, PartialTake) | (PartialTake, PartialFreeze) | + (TotalFreeze, PartialFreeze) | (PartialFreeze, TotalFreeze) | + (Immobile, _) | (_, Immobile) | + (PartialFreeze, PartialFreeze) | + (PartialTake, PartialTake) | + (TotalFreeze, TotalFreeze) => { + /* ok */ } - (m_mutbl, m_mutbl) | (m_mutbl, m_imm) | (m_imm, m_mutbl) => { + (PartialTake, TotalFreeze) | (TotalFreeze, PartialTake) | + (TotalTake, TotalFreeze) | (TotalFreeze, TotalTake) | + (TotalTake, PartialFreeze) | (PartialFreeze, TotalTake) | + (TotalTake, PartialTake) | (PartialTake, TotalTake) | + (TotalTake, TotalTake) => { self.bccx.span_err( new_loan.cmt.span, fmt!("loan of %s as %s \ conflicts with prior loan", self.bccx.cmt_to_str(new_loan.cmt), - self.bccx.mut_to_str(new_loan.mutbl))); + self.bccx.loan_kind_to_str(new_loan.kind))); self.bccx.span_note( old_loan.cmt.span, fmt!("prior loan as %s granted here", - self.bccx.mut_to_str(old_loan.mutbl))); + self.bccx.loan_kind_to_str(old_loan.kind))); } } } @@ -348,13 +357,13 @@ impl CheckLoanCtxt { // are only assigned once } else { match cmt.mutbl { - m_mutbl => { /*ok*/ } - m_const | m_imm => { - self.bccx.span_err( - ex.span, - at.ing_form(self.bccx.cmt_to_str(cmt))); - return; - } + McDeclared | McInherited => { /*ok*/ } + McReadOnly | McImmutable => { + self.bccx.span_err( + ex.span, + at.ing_form(self.bccx.cmt_to_str(cmt))); + return; + } } } @@ -428,19 +437,20 @@ impl CheckLoanCtxt { cmt: cmt, lp: @loan_path) { for self.walk_loans_of(ex.id, lp) |loan| { - match loan.mutbl { - m_const => { /*ok*/ } - m_mutbl | m_imm => { - self.bccx.span_err( - ex.span, - fmt!("%s prohibited due to outstanding loan", - at.ing_form(self.bccx.cmt_to_str(cmt)))); - self.bccx.span_note( - loan.cmt.span, - fmt!("loan of %s granted here", - self.bccx.cmt_to_str(loan.cmt))); - return; - } + match loan.kind { + Immobile => { /* ok */ } + TotalFreeze | PartialFreeze | + TotalTake | PartialTake => { + self.bccx.span_err( + ex.span, + fmt!("%s prohibited due to outstanding loan", + at.ing_form(self.bccx.cmt_to_str(cmt)))); + self.bccx.span_note( + loan.cmt.span, + fmt!("loan of %s granted here", + self.bccx.cmt_to_str(loan.cmt))); + return; + } } } diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs index d4c45828858..65518398eb7 100644 --- a/src/librustc/middle/borrowck/gather_loans.rs +++ b/src/librustc/middle/borrowck/gather_loans.rs @@ -20,7 +20,10 @@ use core::prelude::*; use middle::borrowck::preserve::{PreserveCondition, PcOk, PcIfPure}; use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl}; +use middle::borrowck::{LoanKind, TotalFreeze, PartialFreeze, + TotalTake, PartialTake, Immobile}; use middle::borrowck::{req_maps}; +use middle::borrowck::loan; use middle::mem_categorization::{cat_binding, cat_discr, cmt, comp_variant}; use middle::mem_categorization::{mem_categorization_ctxt}; use middle::mem_categorization::{opt_deref_kind}; @@ -340,13 +343,22 @@ impl GatherLoanCtxt { fn guarantee_valid(@mut self, cmt: cmt, req_mutbl: ast::mutability, - scope_r: ty::Region) { + scope_r: ty::Region) + { + + let loan_kind = match req_mutbl { + m_mutbl => TotalTake, + m_imm => TotalFreeze, + m_const => Immobile + }; self.bccx.stats.guaranteed_paths += 1; - debug!("guarantee_valid(cmt=%s, req_mutbl=%s, scope_r=%s)", + debug!("guarantee_valid(cmt=%s, req_mutbl=%?, \ + loan_kind=%?, scope_r=%s)", self.bccx.cmt_to_repr(cmt), - self.bccx.mut_to_str(req_mutbl), + req_mutbl, + loan_kind, region_to_str(self.tcx(), scope_r)); let _i = indenter(); @@ -362,10 +374,10 @@ impl GatherLoanCtxt { // it within that scope, the loan will be detected and an // error will be reported. Some(_) => { - match self.bccx.loan(cmt, scope_r, req_mutbl) { + match loan::loan(self.bccx, cmt, scope_r, loan_kind) { Err(ref e) => { self.bccx.report((*e)); } Ok(move loans) => { - self.add_loans(cmt, req_mutbl, scope_r, move loans); + self.add_loans(cmt, loan_kind, scope_r, move loans); } } } @@ -378,7 +390,7 @@ impl GatherLoanCtxt { // pointer is desired, that is ok as long as we are pure) None => { let result: bckres = { - do self.check_mutbl(req_mutbl, cmt).chain |pc1| { + do self.check_mutbl(loan_kind, cmt).chain |pc1| { do self.bccx.preserve(cmt, scope_r, self.item_ub, self.root_ub).chain |pc2| { @@ -446,37 +458,41 @@ impl GatherLoanCtxt { // reqires an immutable pointer, but `f` lives in (aliased) // mutable memory. fn check_mutbl(@mut self, - req_mutbl: ast::mutability, + loan_kind: LoanKind, cmt: cmt) -> bckres { - debug!("check_mutbl(req_mutbl=%?, cmt.mutbl=%?)", - req_mutbl, cmt.mutbl); + debug!("check_mutbl(loan_kind=%?, cmt.mutbl=%?)", + loan_kind, cmt.mutbl); - if req_mutbl == m_const || req_mutbl == cmt.mutbl { - debug!("required is const or they are the same"); - Ok(PcOk) - } else { - let e = bckerr { cmt: cmt, code: err_mutbl(req_mutbl) }; - if req_mutbl == m_imm { - // if this is an @mut box, then it's generally OK to borrow as - // &imm; this will result in a write guard - if cmt.cat.is_mutable_box() { + match loan_kind { + Immobile => Ok(PcOk), + + TotalTake | PartialTake => { + if cmt.mutbl.is_mutable() { Ok(PcOk) } else { - // you can treat mutable things as imm if you are pure - debug!("imm required, must be pure"); + Err(bckerr { cmt: cmt, code: err_mutbl(loan_kind) }) + } + } + TotalFreeze | PartialFreeze => { + if cmt.mutbl.is_immutable() { + Ok(PcOk) + } else if cmt.cat.is_mutable_box() { + Ok(PcOk) + } else { + // Eventually: + let e = bckerr {cmt: cmt, + code: err_mutbl(loan_kind)}; Ok(PcIfPure(e)) } - } else { - Err(e) } } } fn add_loans(@mut self, cmt: cmt, - req_mutbl: ast::mutability, + loan_kind: LoanKind, scope_r: ty::Region, +loans: ~[Loan]) { if loans.len() == 0 { @@ -526,7 +542,7 @@ impl GatherLoanCtxt { self.add_loans_to_scope_id(scope_id, move loans); - if req_mutbl == m_imm && cmt.mutbl != m_imm { + if loan_kind.is_freeze() && !cmt.mutbl.is_immutable() { self.bccx.stats.loaned_paths_imm += 1; if self.tcx().sess.borrowck_note_loan() { @@ -542,7 +558,9 @@ impl GatherLoanCtxt { fn add_loans_to_scope_id(@mut self, scope_id: ast::node_id, +loans: ~[Loan]) { - debug!("adding %u loans to scope_id %?", loans.len(), scope_id); + debug!("adding %u loans to scope_id %?: %s", + loans.len(), scope_id, + str::connect(loans.map(|l| self.bccx.loan_to_repr(l)), ", ")); match self.req_maps.req_loan_map.find(&scope_id) { Some(req_loans) => { req_loans.push_all(loans); diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs index da22b20fd1d..3b6d735ae78 100644 --- a/src/librustc/middle/borrowck/loan.rs +++ b/src/librustc/middle/borrowck/loan.rs @@ -44,6 +44,8 @@ FIXME #4730 --- much more needed, don't have time to write this all up now use core::prelude::*; use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl}; +use middle::borrowck::{LoanKind, TotalFreeze, PartialFreeze, + TotalTake, PartialTake, Immobile}; use middle::borrowck::{err_out_of_scope}; use middle::mem_categorization::{cat_arg, cat_binding, cat_discr, cat_comp}; use middle::mem_categorization::{cat_deref, cat_discr, cat_local, cat_self}; @@ -57,27 +59,26 @@ use core::result::{Err, Ok, Result}; use syntax::ast::{m_const, m_imm, m_mutbl}; use syntax::ast; -impl BorrowckCtxt { - fn loan(&self, +pub fn loan(bccx: @BorrowckCtxt, cmt: cmt, scope_region: ty::Region, - mutbl: ast::mutability) -> bckres<~[Loan]> { - let mut lc = LoanContext { - bccx: self, - scope_region: scope_region, - loans: ~[] - }; - match lc.loan(cmt, mutbl, true) { - Err(ref e) => return Err((*e)), - Ok(()) => {} - } - // XXX: Workaround for borrow check bug. - Ok(copy lc.loans) + loan_kind: LoanKind) -> bckres<~[Loan]> +{ + let mut lc = LoanContext { + bccx: bccx, + scope_region: scope_region, + loans: ~[] + }; + match lc.loan(cmt, loan_kind, true) { + Err(ref e) => return Err((*e)), + Ok(()) => {} } + // XXX: Workaround for borrow check bug. + Ok(copy lc.loans) } struct LoanContext { - bccx: &BorrowckCtxt, + bccx: @BorrowckCtxt, // the region scope for which we must preserve the memory scope_region: ty::Region, @@ -87,12 +88,13 @@ struct LoanContext { } impl LoanContext { - fn tcx(&mut self) -> ty::ctxt { self.bccx.tcx } + fn tcx(&self) -> ty::ctxt { self.bccx.tcx } fn loan(&mut self, cmt: cmt, - req_mutbl: ast::mutability, - owns_lent_data: bool) -> bckres<()> { + loan_kind: LoanKind, + owns_lent_data: bool) -> bckres<()> + { /*! * * The main routine. @@ -107,9 +109,9 @@ impl LoanContext { * discussion in `issue_loan()`. */ - debug!("loan(%s, %s)", + debug!("loan(%s, %?)", self.bccx.cmt_to_repr(cmt), - self.bccx.mut_to_str(req_mutbl)); + loan_kind); let _i = indenter(); // see stable() above; should only be called when `cmt` is lendable @@ -127,15 +129,16 @@ impl LoanContext { ~"rvalue with a non-none lp"); } cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => { - let local_scope_id = self.tcx().region_map.get(&local_id); - self.issue_loan(cmt, ty::re_scope(local_scope_id), req_mutbl, + // FIXME(#4903) + let local_scope_id = self.bccx.tcx.region_map.get(&local_id); + self.issue_loan(cmt, ty::re_scope(local_scope_id), loan_kind, owns_lent_data) } cat_stack_upvar(cmt) => { - self.loan(cmt, req_mutbl, owns_lent_data) + self.loan(cmt, loan_kind, owns_lent_data) } cat_discr(base, _) => { - self.loan(base, req_mutbl, owns_lent_data) + self.loan(base, loan_kind, owns_lent_data) } cat_comp(cmt_base, comp_field(_, m)) | cat_comp(cmt_base, comp_index(_, m)) => { @@ -145,13 +148,13 @@ impl LoanContext { // that case, it must also be embedded in an immutable // location, or else the whole structure could be // overwritten and the component along with it. - self.loan_stable_comp(cmt, cmt_base, req_mutbl, m, + self.loan_stable_comp(cmt, cmt_base, loan_kind, m, owns_lent_data) } cat_comp(cmt_base, comp_tuple) | cat_comp(cmt_base, comp_anon_field) => { // As above. - self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm, + self.loan_stable_comp(cmt, cmt_base, loan_kind, m_imm, owns_lent_data) } cat_comp(cmt_base, comp_variant(enum_did)) => { @@ -159,10 +162,10 @@ impl LoanContext { // variants, because if the enum value is overwritten then // the memory changes type. if ty::enum_is_univariant(self.bccx.tcx, enum_did) { - self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm, + self.loan_stable_comp(cmt, cmt_base, loan_kind, m_imm, owns_lent_data) } else { - self.loan_unstable_deref(cmt, cmt_base, req_mutbl, + self.loan_unstable_deref(cmt, cmt_base, loan_kind, owns_lent_data) } } @@ -170,7 +173,7 @@ impl LoanContext { // For unique pointers, the memory being pointed out is // unstable because if the unique pointer is overwritten // then the memory is freed. - self.loan_unstable_deref(cmt, cmt_base, req_mutbl, + self.loan_unstable_deref(cmt, cmt_base, loan_kind, owns_lent_data) } cat_deref(cmt_base, _, region_ptr(ast::m_mutbl, region)) => { @@ -178,8 +181,8 @@ impl LoanContext { // loan out the base as well as the main memory. For example, // if someone borrows `*b`, we want to borrow `b` as immutable // as well. - do self.loan(cmt_base, m_imm, false).chain |_| { - self.issue_loan(cmt, region, m_const, owns_lent_data) + do self.loan(cmt_base, TotalFreeze, false).chain |_| { + self.issue_loan(cmt, region, loan_kind, owns_lent_data) } } cat_deref(_, _, unsafe_ptr) | @@ -199,66 +202,38 @@ impl LoanContext { fn loan_stable_comp(&mut self, cmt: cmt, cmt_base: cmt, - req_mutbl: ast::mutability, + loan_kind: LoanKind, comp_mutbl: ast::mutability, - owns_lent_data: bool) -> bckres<()> { - // Determine the mutability that the base component must have, - // given the required mutability of the pointer (`req_mutbl`) - // and the declared mutability of the component (`comp_mutbl`). - // This is surprisingly subtle. - // - // Note that the *declared* mutability of the component is not - // necessarily the same as cmt.mutbl, since a component - // declared as immutable but embedded in a mutable context - // becomes mutable. It's best to think of comp_mutbl as being - // either MUTABLE or DEFAULT, not MUTABLE or IMMUTABLE. We - // should really patch up the AST to reflect this distinction. - // - // Let's consider the cases below: - // - // 1. mut required, mut declared: In this case, the base - // component must merely be const. The reason is that it - // does not matter if the base component is borrowed as - // mutable or immutable, as the mutability of the base - // component is overridden in the field declaration itself - // (see `compile-fail/borrowck-mut-field-imm-base.rs`) - // - // 2. mut required, imm declared: This would only be legal if - // the component is embeded in a mutable context. However, - // we detect mismatches between the mutability of the value - // as a whole and the required mutability in `issue_loan()` - // above. In any case, presuming that the component IS - // embedded in a mutable context, both the component and - // the base must be loaned as MUTABLE. This is to ensure - // that there is no loan of the base as IMMUTABLE, which - // would imply that the component must be IMMUTABLE too - // (see `compile-fail/borrowck-imm-field-imm-base.rs`). - // - // 3. mut required, const declared: this shouldn't really be - // possible, since I don't think you can declare a const - // field, but I guess if we DID permit such a declaration - // it would be equivalent to the case above? - // - // 4. imm required, * declared: In this case, the base must be - // immutable. This is true regardless of what was declared - // for this subcomponent, this if the base is mutable, the - // subcomponent must be mutable. - // (see `compile-fail/borrowck-imm-field-mut-base.rs`). - // - // 5. const required, * declared: In this case, the base need - // only be const, since we don't ultimately care whether - // the subcomponent is mutable or not. - let base_mutbl = match (req_mutbl, comp_mutbl) { - (m_mutbl, m_mutbl) => m_const, // (1) - (m_mutbl, _) => m_mutbl, // (2, 3) - (m_imm, _) => m_imm, // (4) - (m_const, _) => m_const // (5) + owns_lent_data: bool) -> bckres<()> + { + let base_kind = match (comp_mutbl, loan_kind) { + // Declared as "immutable" means: inherited mutability and + // hence mutable iff parent is mutable. So propagate + // mutability on up. + (m_imm, TotalFreeze) | (m_imm, PartialFreeze) => PartialFreeze, + (m_imm, TotalTake) | (m_imm, PartialTake) => PartialTake, + + // Declared as "mutable" means: always mutable no matter + // what the mutability of the base is. So that means we + // can weaken the condition on the base to PartialFreeze. + // This implies that the user could freeze the base, but + // that is ok since the even with an &T base, the mut + // field will still be considered mutable. + (_, TotalTake) | (_, PartialTake) | + (_, TotalFreeze) | (_, PartialFreeze) => { + PartialFreeze + } + + // If we just need to guarantee the value won't be moved, + // it doesn't matter what mutability the component was + // declared with. + (_, Immobile) => Immobile, }; - do self.loan(cmt_base, base_mutbl, owns_lent_data).chain |_ok| { + do self.loan(cmt_base, base_kind, owns_lent_data).chain |_ok| { // can use static for the scope because the base // determines the lifetime, ultimately - self.issue_loan(cmt, ty::re_static, req_mutbl, + self.issue_loan(cmt, ty::re_static, loan_kind, owns_lent_data) } } @@ -269,23 +244,23 @@ impl LoanContext { fn loan_unstable_deref(&mut self, cmt: cmt, cmt_base: cmt, - req_mutbl: ast::mutability, + loan_kind: LoanKind, owns_lent_data: bool) -> bckres<()> { // Variant components: the base must be immutable, because // if it is overwritten, the types of the embedded data // could change. - do self.loan(cmt_base, m_imm, owns_lent_data).chain |_| { + do self.loan(cmt_base, PartialFreeze, owns_lent_data).chain |_| { // can use static, as in loan_stable_comp() - self.issue_loan(cmt, ty::re_static, req_mutbl, + self.issue_loan(cmt, ty::re_static, loan_kind, owns_lent_data) } } fn issue_loan(&mut self, - cmt: cmt, - scope_ub: ty::Region, - req_mutbl: ast::mutability, - owns_lent_data: bool) -> bckres<()> { + +cmt: cmt, + +scope_ub: ty::Region, + +loan_kind: LoanKind, + +owns_lent_data: bool) -> bckres<()> { // Subtle: the `scope_ub` is the maximal lifetime of `cmt`. // Therefore, if `cmt` owns the data being lent, then the // scope of the loan must be less than `scope_ub`, or else the @@ -297,25 +272,15 @@ impl LoanContext { // reborrowed. if !owns_lent_data || - self.bccx.is_subregion_of(/*bad*/copy self.scope_region, - scope_ub) { - match req_mutbl { - m_mutbl => { - // We do not allow non-mutable data to be loaned - // out as mutable under any circumstances. - if cmt.mutbl != m_mutbl { - return Err(bckerr { - cmt:cmt, - code:err_mutbl(req_mutbl) - }); - } - } - m_const | m_imm => { - // However, mutable data can be loaned out as - // immutable (and any data as const). The - // `check_loans` pass will then guarantee that no - // writes occur for the duration of the loan. - } + self.bccx.is_subregion_of(self.scope_region, scope_ub) + { + if loan_kind.is_take() && !cmt.mutbl.is_mutable() { + // We do not allow non-mutable data to be "taken" + // under any circumstances. + return Err(bckerr { + cmt:cmt, + code:err_mutbl(loan_kind) + }); } self.loans.push(Loan { @@ -323,8 +288,9 @@ impl LoanContext { // loan process does not apply at all. lp: cmt.lp.get(), cmt: cmt, - mutbl: req_mutbl + kind: loan_kind }); + return Ok(()); } else { // The loan being requested lives longer than the data diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 3d45ee461dc..dd8f889a057 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -368,7 +368,7 @@ pub enum bckerr_code { err_mut_uniq, err_mut_variant, err_root_not_permitted, - err_mutbl(ast::mutability), + err_mutbl(LoanKind), err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope err_out_of_scope(ty::Region, ty::Region) // superscope, subscope } @@ -390,8 +390,19 @@ pub enum MoveError { // shorthand for something that fails with `bckerr` or succeeds with `T` pub type bckres = Result; +#[deriving_eq] +pub enum LoanKind { + TotalFreeze, // Entire path is frozen (borrowed as &T) + PartialFreeze, // Some subpath is frozen (borrowed as &T) + TotalTake, // Entire path is "taken" (borrowed as &mut T) + PartialTake, // Some subpath is "taken" (borrowed as &mut T) + Immobile // Path cannot be moved (borrowed as &const T) +} + /// a complete record of a loan that was granted -pub struct Loan {lp: @loan_path, cmt: cmt, mutbl: ast::mutability} +pub struct Loan {lp: @loan_path, + cmt: cmt, + kind: LoanKind} /// maps computed by `gather_loans` that are then used by `check_loans` /// @@ -420,6 +431,22 @@ pub fn save_and_restore_managed(save_and_restore_t: @mut T, move u } +impl LoanKind { + fn is_freeze(&self) -> bool { + match *self { + TotalFreeze | PartialFreeze => true, + _ => false + } + } + + fn is_take(&self) -> bool { + match *self { + TotalTake | PartialTake => true, + _ => false + } + } +} + /// Creates and returns a new root_map pub impl root_map_key : to_bytes::IterBytes { @@ -520,9 +547,9 @@ pub impl BorrowckCtxt { fn bckerr_to_str(&self, err: bckerr) -> ~str { match err.code { - err_mutbl(req) => { + err_mutbl(lk) => { fmt!("creating %s alias to %s", - self.mut_to_str(req), + self.loan_kind_to_str(lk), self.cmt_to_str(err.cmt)) } err_mut_uniq => { @@ -599,9 +626,17 @@ pub impl BorrowckCtxt { mc.mut_to_str(mutbl) } + fn loan_kind_to_str(&self, lk: LoanKind) -> ~str { + match lk { + TotalFreeze | PartialFreeze => ~"immutable", + TotalTake | PartialTake => ~"mutable", + Immobile => ~"read-only" + } + } + fn loan_to_repr(&self, loan: &Loan) -> ~str { - fmt!("Loan(lp=%?, cmt=%s, mutbl=%?)", - loan.lp, self.cmt_to_repr(loan.cmt), loan.mutbl) + fmt!("Loan(lp=%?, cmt=%s, kind=%?)", + loan.lp, self.cmt_to_repr(loan.cmt), loan.kind) } } diff --git a/src/librustc/middle/borrowck/preserve.rs b/src/librustc/middle/borrowck/preserve.rs index 097f0579362..a123793e20b 100644 --- a/src/librustc/middle/borrowck/preserve.rs +++ b/src/librustc/middle/borrowck/preserve.rs @@ -190,10 +190,10 @@ impl PreserveCtxt { // otherwise we have no guarantee the pointer will stay // live, so we must root the pointer (i.e., inc the ref // count) for the duration of the loan. - debug!("base.mutbl = %?", self.bccx.mut_to_str(base.mutbl)); + debug!("base.mutbl = %?", base.mutbl); if cmt.cat.derefs_through_mutable_box() { self.attempt_root(cmt, base, derefs) - } else if base.mutbl == m_imm { + } else if base.mutbl.is_immutable() { let non_rooting_ctxt = PreserveCtxt { root_managed_data: false, ..*self @@ -293,14 +293,11 @@ impl PreserveCtxt { // the base is preserved, but if we are not mutable then // purity is required Ok(PcOk) => { - match cmt_base.mutbl { - m_mutbl | m_const => { - Ok(PcIfPure(bckerr {cmt:cmt, code:code})) + if !cmt_base.mutbl.is_immutable() { + Ok(PcIfPure(bckerr {cmt:cmt, code:code})) + } else { + Ok(PcOk) } - m_imm => { - Ok(PcOk) - } - } } // the base requires purity too, that's fine diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index fd9271af6c6..c4612e76024 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -108,6 +108,14 @@ pub enum special_kind { sk_heap_upvar } +#[deriving_eq] +pub enum MutabilityCategory { + McImmutable, // Immutable. + McReadOnly, // Read-only (`const`) + McDeclared, // Directly declared as mutable. + McInherited // Inherited from the fact that owner is mutable. +} + // a complete categorization of a value indicating where it originated // and how it is located, as well as the mutability of the memory in // which the value is stored. @@ -115,12 +123,12 @@ pub enum special_kind { // note: cmt stands for "categorized mutable type". #[deriving_eq] pub struct cmt_ { - id: ast::node_id, // id of expr/pat producing this value - span: span, // span of same expr/pat - cat: categorization, // categorization of expr - lp: Option<@loan_path>, // loan path for expr, if any - mutbl: ast::mutability, // mutability of expr as lvalue - ty: ty::t // type of the expr + id: ast::node_id, // id of expr/pat producing this value + span: span, // span of same expr/pat + cat: categorization, // categorization of expr + lp: Option<@loan_path>, // loan path for expr, if any + mutbl: MutabilityCategory, // mutability of expr as lvalue + ty: ty::t // type of the expr } pub type cmt = @cmt_; @@ -298,8 +306,55 @@ pub struct mem_categorization_ctxt { method_map: typeck::method_map, } -pub impl &mem_categorization_ctxt { - fn cat_expr(expr: @ast::expr) -> cmt { +impl ToStr for MutabilityCategory { + pure fn to_str(&self) -> ~str { + fmt!("%?", *self) + } +} + +impl MutabilityCategory { + static fn from_mutbl(m: ast::mutability) -> MutabilityCategory { + match m { + m_imm => McImmutable, + m_const => McReadOnly, + m_mutbl => McDeclared + } + } + + fn inherit(&self) -> MutabilityCategory { + match *self { + McImmutable => McImmutable, + McReadOnly => McReadOnly, + McDeclared => McInherited, + McInherited => McInherited + } + } + + fn is_mutable(&self) -> bool { + match *self { + McImmutable | McReadOnly => false, + McDeclared | McInherited => true + } + } + + fn is_immutable(&self) -> bool { + match *self { + McImmutable => true, + McReadOnly | McDeclared | McInherited => false + } + } + + fn to_user_str(&self) -> ~str { + match *self { + McDeclared | McInherited => ~"mutable", + McImmutable => ~"immutable", + McReadOnly => ~"const" + } + } +} + +pub impl mem_categorization_ctxt { + fn cat_expr(&self, expr: @ast::expr) -> cmt { match self.tcx.adjustments.find(&expr.id) { None => { // No adjustments. @@ -323,7 +378,8 @@ pub impl &mem_categorization_ctxt { } } - fn cat_expr_autoderefd(expr: @ast::expr, + fn cat_expr_autoderefd(&self, + expr: @ast::expr, adjustment: &ty::AutoAdjustment) -> cmt { let mut cmt = self.cat_expr_unadjusted(expr); for uint::range(1, adjustment.autoderefs+1) |deref| { @@ -332,7 +388,7 @@ pub impl &mem_categorization_ctxt { return cmt; } - fn cat_expr_unadjusted(expr: @ast::expr) -> cmt { + fn cat_expr_unadjusted(&self, expr: @ast::expr) -> cmt { debug!("cat_expr: id=%d expr=%s", expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr())); @@ -392,7 +448,8 @@ pub impl &mem_categorization_ctxt { } } - fn cat_def(id: ast::node_id, + fn cat_def(&self, + id: ast::node_id, span: span, expr_ty: ty::t, def: ast::def) -> cmt { @@ -409,7 +466,7 @@ pub impl &mem_categorization_ctxt { span:span, cat:cat_special(sk_static_item), lp:None, - mutbl:m_imm, + mutbl: McImmutable, ty:expr_ty } } @@ -420,7 +477,7 @@ pub impl &mem_categorization_ctxt { // m: mutability of the argument // lp: loan path, must be none for aliasable things - let m = if mutbl {m_mutbl} else {m_imm}; + let m = if mutbl {McDeclared} else {McImmutable}; let lp = match ty::resolved_mode(self.tcx, mode) { ast::by_copy => Some(@lp_arg(vid)), ast::by_ref => None, @@ -438,7 +495,7 @@ pub impl &mem_categorization_ctxt { span:span, cat:cat_arg(vid), lp:lp, - mutbl:m, + mutbl: m, ty:expr_ty } } @@ -458,7 +515,7 @@ pub impl &mem_categorization_ctxt { span:span, cat:cat, lp:loan_path, - mutbl:m_imm, + mutbl: McImmutable, ty:expr_ty } } @@ -485,7 +542,7 @@ pub impl &mem_categorization_ctxt { span:span, cat:cat_special(sk_heap_upvar), lp:None, - mutbl:m_imm, + mutbl:McImmutable, ty:expr_ty } } @@ -493,7 +550,7 @@ pub impl &mem_categorization_ctxt { } ast::def_local(vid, mutbl) => { - let m = if mutbl {m_mutbl} else {m_imm}; + let m = if mutbl {McDeclared} else {McImmutable}; @cmt_ { id:id, span:span, @@ -511,14 +568,15 @@ pub impl &mem_categorization_ctxt { span:span, cat:cat_local(vid), lp:Some(@lp_local(vid)), - mutbl:m_imm, + mutbl:McImmutable, ty:expr_ty } } } } - fn cat_variant(arg: N, + fn cat_variant(&self, + arg: N, enum_did: ast::def_id, cmt: cmt) -> cmt { @cmt_ { @@ -526,18 +584,18 @@ pub impl &mem_categorization_ctxt { span: arg.span(), cat: cat_comp(cmt, comp_variant(enum_did)), lp: cmt.lp.map(|l| @lp_comp(*l, comp_variant(enum_did)) ), - mutbl: cmt.mutbl, // imm iff in an immutable context + mutbl: cmt.mutbl.inherit(), ty: self.tcx.ty(arg) } } - fn cat_rvalue(elt: N, expr_ty: ty::t) -> cmt { + fn cat_rvalue(&self, elt: N, expr_ty: ty::t) -> cmt { @cmt_ { id:elt.id(), span:elt.span(), cat:cat_rvalue, lp:None, - mutbl:m_imm, + mutbl:McImmutable, ty:expr_ty } } @@ -546,17 +604,21 @@ pub impl &mem_categorization_ctxt { /// component is inherited from the base it is a part of. For /// example, a record field is mutable if it is declared mutable /// or if the container is mutable. - fn inherited_mutability(base_m: ast::mutability, - comp_m: ast::mutability) -> ast::mutability { + fn inherited_mutability(&self, + base_m: MutabilityCategory, + comp_m: ast::mutability) -> MutabilityCategory + { match comp_m { - m_imm => {base_m} // imm: as mutable as the container - m_mutbl | m_const => {comp_m} + m_imm => base_m.inherit(), + m_const => McReadOnly, + m_mutbl => McDeclared } } /// The `field_id` parameter is the ID of the enclosing expression or /// pattern. It is used to determine which variant of an enum is in use. - fn cat_field(node: N, + fn cat_field(&self, + node: N, base_cmt: cmt, f_name: ast::ident, field_id: ast::node_id) -> cmt { @@ -584,7 +646,8 @@ pub impl &mem_categorization_ctxt { } } - fn cat_deref_fn(node: N, + fn cat_deref_fn(&self, + node: N, base_cmt: cmt, deref_cnt: uint) -> cmt { @@ -594,11 +657,13 @@ pub impl &mem_categorization_ctxt { // know what type lies at the other end, so we just call it // `()` (the empty tuple). - let mt = ty::mt {ty: ty::mk_tup(self.tcx, ~[]), mutbl: m_imm}; + let mt = ty::mt {ty: ty::mk_tup(self.tcx, ~[]), + mutbl: m_imm}; return self.cat_deref_common(node, base_cmt, deref_cnt, mt); } - fn cat_deref(node: N, + fn cat_deref(&self, + node: N, base_cmt: cmt, deref_cnt: uint) -> cmt { @@ -615,7 +680,8 @@ pub impl &mem_categorization_ctxt { return self.cat_deref_common(node, base_cmt, deref_cnt, mt); } - fn cat_deref_common(node: N, + fn cat_deref_common(&self, + node: N, base_cmt: cmt, deref_cnt: uint, mt: ty::mt) -> cmt @@ -644,7 +710,7 @@ pub impl &mem_categorization_ctxt { self.inherited_mutability(base_cmt.mutbl, mt.mutbl) } gc_ptr(*) | region_ptr(_, _) | unsafe_ptr => { - mt.mutbl + MutabilityCategory::from_mutbl(mt.mutbl) } }; @@ -673,7 +739,9 @@ pub impl &mem_categorization_ctxt { } } - fn cat_index(elt: N, base_cmt: cmt) -> cmt { + fn cat_index(&self, + elt: N, + base_cmt: cmt) -> cmt { let mt = match ty::index(self.tcx, base_cmt.ty) { Some(mt) => mt, None => { @@ -700,7 +768,7 @@ pub impl &mem_categorization_ctxt { self.inherited_mutability(base_cmt.mutbl, mt.mutbl) } gc_ptr(_) | region_ptr(_, _) | unsafe_ptr => { - mt.mutbl + MutabilityCategory::from_mutbl(mt.mutbl) } }; @@ -714,21 +782,21 @@ pub impl &mem_categorization_ctxt { ty:mt.ty }; - comp(elt, deref_cmt, base_cmt.ty, m, mt.ty) + comp(elt, deref_cmt, base_cmt.ty, m, mt) } deref_comp(_) => { // fixed-length vectors have no deref let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl); - comp(elt, base_cmt, base_cmt.ty, m, mt.ty) + comp(elt, base_cmt, base_cmt.ty, m, mt) } }; fn comp(elt: N, of_cmt: cmt, - vect: ty::t, mutbl: ast::mutability, - ty: ty::t) -> cmt + vect: ty::t, mutbl: MutabilityCategory, + mt: ty::mt) -> cmt { - let comp = comp_index(vect, mutbl); + let comp = comp_index(vect, mt.mutbl); let index_lp = of_cmt.lp.map(|lp| @lp_comp(*lp, comp) ); @cmt_ { id:elt.id(), @@ -736,46 +804,55 @@ pub impl &mem_categorization_ctxt { cat:cat_comp(of_cmt, comp), lp:index_lp, mutbl:mutbl, - ty:ty + ty:mt.ty } } } - fn cat_tuple_elt(elt: N, cmt: cmt) -> cmt { + fn cat_tuple_elt(&self, + elt: N, + cmt: cmt) -> cmt { @cmt_ { id: elt.id(), span: elt.span(), cat: cat_comp(cmt, comp_tuple), lp: cmt.lp.map(|l| @lp_comp(*l, comp_tuple) ), - mutbl: cmt.mutbl, // imm iff in an immutable context + mutbl: cmt.mutbl.inherit(), ty: self.tcx.ty(elt) } } - fn cat_anon_struct_field(elt: N, cmt: cmt) -> cmt { + fn cat_anon_struct_field(&self, + elt: N, + cmt: cmt) -> cmt { @cmt_ { id: elt.id(), span: elt.span(), cat: cat_comp(cmt, comp_anon_field), lp: cmt.lp.map(|l| @lp_comp(*l, comp_anon_field)), - mutbl: cmt.mutbl, // imm iff in an immutable context + mutbl: cmt.mutbl.inherit(), ty: self.tcx.ty(elt) } } - fn cat_method_ref(expr: @ast::expr, expr_ty: ty::t) -> cmt { + fn cat_method_ref(&self, + expr: @ast::expr, + expr_ty: ty::t) -> cmt { @cmt_ { id:expr.id, span:expr.span, cat:cat_special(sk_method), lp:None, - mutbl:m_imm, + mutbl:McImmutable, ty:expr_ty } } - fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) { - + fn cat_pattern(&self, + cmt: cmt, + pat: @ast::pat, + op: fn(cmt, @ast::pat)) + { // Here, `cmt` is the categorization for the value being // matched and pat is the pattern it is being matched against. // @@ -901,7 +978,7 @@ pub impl &mem_categorization_ctxt { } } - fn cat_to_repr(cat: categorization) -> ~str { + fn cat_to_repr(&self, cat: categorization) -> ~str { match cat { cat_special(sk_method) => ~"method", cat_special(sk_static_item) => ~"static_item", @@ -924,7 +1001,7 @@ pub impl &mem_categorization_ctxt { } } - fn mut_to_str(mutbl: ast::mutability) -> ~str { + fn mut_to_str(&self, mutbl: ast::mutability) -> ~str { match mutbl { m_mutbl => ~"mutable", m_const => ~"const", @@ -932,7 +1009,7 @@ pub impl &mem_categorization_ctxt { } } - fn ptr_sigil(ptr: ptr_kind) -> ~str { + fn ptr_sigil(&self, ptr: ptr_kind) -> ~str { match ptr { uniq_ptr => ~"~", gc_ptr(_) => ~"@", @@ -941,7 +1018,7 @@ pub impl &mem_categorization_ctxt { } } - fn comp_to_repr(comp: comp_kind) -> ~str { + fn comp_to_repr(&self, comp: comp_kind) -> ~str { match comp { comp_field(fld, _) => self.tcx.sess.str_of(fld), comp_index(*) => ~"[]", @@ -951,7 +1028,7 @@ pub impl &mem_categorization_ctxt { } } - fn lp_to_str(lp: @loan_path) -> ~str { + fn lp_to_str(&self, lp: @loan_path) -> ~str { match *lp { lp_local(node_id) => { fmt!("local(%d)", node_id) @@ -971,17 +1048,17 @@ pub impl &mem_categorization_ctxt { } } - fn cmt_to_repr(cmt: cmt) -> ~str { - fmt!("{%s id:%d m:%s lp:%s ty:%s}", + fn cmt_to_repr(&self, cmt: cmt) -> ~str { + fmt!("{%s id:%d m:%? lp:%s ty:%s}", self.cat_to_repr(cmt.cat), cmt.id, - self.mut_to_str(cmt.mutbl), + cmt.mutbl, cmt.lp.map_default(~"none", |p| self.lp_to_str(*p) ), ty_to_str(self.tcx, cmt.ty)) } - fn cmt_to_str(cmt: cmt) -> ~str { - let mut_str = self.mut_to_str(cmt.mutbl); + fn cmt_to_str(&self, cmt: cmt) -> ~str { + let mut_str = cmt.mutbl.to_user_str(); match cmt.cat { cat_special(sk_method) => ~"method", cat_special(sk_static_item) => ~"static item", @@ -1016,7 +1093,7 @@ pub impl &mem_categorization_ctxt { } } - fn region_to_str(r: ty::Region) -> ~str { + fn region_to_str(&self, r: ty::Region) -> ~str { region_to_str(self.tcx, r) } } diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index 16677530ecd..83fcc175837 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -116,26 +116,26 @@ pub impl Reflector { fn bracketed(&mut self, bracket_name: ~str, +extra: ~[ValueRef], - inner: &fn()) { + inner: &fn(&mut Reflector)) { // XXX: Bad copy. self.visit(~"enter_" + bracket_name, copy extra); - inner(); + inner(self); self.visit(~"leave_" + bracket_name, extra); } fn vstore_name_and_extra(&mut self, t: ty::t, - vstore: ty::vstore, - f: fn(+s: ~str,+v: ~[ValueRef])) { + vstore: ty::vstore) -> (~str, ~[ValueRef]) + { match vstore { - ty::vstore_fixed(n) => { - let extra = vec::append(~[self.c_uint(n)], - self.c_size_and_align(t)); - f(~"fixed", extra) - } - ty::vstore_slice(_) => f(~"slice", ~[]), - ty::vstore_uniq => f(~"uniq", ~[]), - ty::vstore_box => f(~"box", ~[]) + ty::vstore_fixed(n) => { + let extra = vec::append(~[self.c_uint(n)], + self.c_size_and_align(t)); + (~"fixed", extra) + } + ty::vstore_slice(_) => (~"slice", ~[]), + ty::vstore_uniq => (~"uniq", ~[]), + ty::vstore_box => (~"box", ~[]) } } @@ -168,47 +168,60 @@ pub impl Reflector { ty::ty_float(ast::ty_f32) => self.leaf(~"f32"), ty::ty_float(ast::ty_f64) => self.leaf(~"f64"), - ty::ty_unboxed_vec(mt) => self.visit(~"vec", self.c_mt(mt)), + ty::ty_unboxed_vec(mt) => { + let values = self.c_mt(mt); + self.visit(~"vec", values) + } + ty::ty_estr(vst) => { - do self.vstore_name_and_extra(t, vst) |name, extra| { - self.visit(~"estr_" + name, extra) - } + let (name, extra) = self.vstore_name_and_extra(t, vst); + self.visit(~"estr_" + name, extra) } ty::ty_evec(mt, vst) => { - do self.vstore_name_and_extra(t, vst) |name, extra| { - self.visit(~"evec_" + name, extra + - self.c_mt(mt)) - } + let (name, extra) = self.vstore_name_and_extra(t, vst); + let extra = extra + self.c_mt(mt); + self.visit(~"evec_" + name, extra) + } + ty::ty_box(mt) => { + let extra = self.c_mt(mt); + self.visit(~"box", extra) + } + ty::ty_uniq(mt) => { + let extra = self.c_mt(mt); + self.visit(~"uniq", extra) + } + ty::ty_ptr(mt) => { + let extra = self.c_mt(mt); + self.visit(~"ptr", extra) + } + ty::ty_rptr(_, mt) => { + let extra = self.c_mt(mt); + self.visit(~"rptr", extra) } - ty::ty_box(mt) => self.visit(~"box", self.c_mt(mt)), - ty::ty_uniq(mt) => self.visit(~"uniq", self.c_mt(mt)), - ty::ty_ptr(mt) => self.visit(~"ptr", self.c_mt(mt)), - ty::ty_rptr(_, mt) => self.visit(~"rptr", self.c_mt(mt)), ty::ty_rec(fields) => { - do self.bracketed(~"rec", - ~[self.c_uint(vec::len(fields))] - + self.c_size_and_align(t)) { + let extra = ~[self.c_uint(vec::len(fields))] + + self.c_size_and_align(t); + do self.bracketed(~"rec", extra) |this| { for fields.eachi |i, field| { - self.visit(~"rec_field", - ~[self.c_uint(i), - self.c_slice( - bcx.ccx().sess.str_of(field.ident))] - + self.c_mt(field.mt)); + let extra = ~[this.c_uint(i), + this.c_slice( + bcx.ccx().sess.str_of(field.ident))] + + this.c_mt(field.mt); + this.visit(~"rec_field", extra); } } } ty::ty_tup(tys) => { - do self.bracketed(~"tup", - ~[self.c_uint(vec::len(tys))] - + self.c_size_and_align(t)) { - for tys.eachi |i, t| { - self.visit(~"tup_field", - ~[self.c_uint(i), - self.c_tydesc(*t)]); - } - } + let extra = ~[self.c_uint(vec::len(tys))] + + self.c_size_and_align(t); + do self.bracketed(~"tup", extra) |this| { + for tys.eachi |i, t| { + let extra = ~[this.c_uint(i), this.c_tydesc(*t)]; + this.visit(~"tup_field", extra); + } + } } // FIXME (#2594): fetch constants out of intrinsic @@ -242,20 +255,21 @@ pub impl Reflector { } ty::ty_struct(did, ref substs) => { - let bcx = self.bcx; - let tcx = bcx.ccx().tcx; - let fields = ty::struct_fields(tcx, did, substs); + let bcx = self.bcx; + let tcx = bcx.ccx().tcx; + let fields = ty::struct_fields(tcx, did, substs); - do self.bracketed(~"class", ~[self.c_uint(fields.len())] - + self.c_size_and_align(t)) { - for fields.eachi |i, field| { - self.visit(~"class_field", - ~[self.c_uint(i), - self.c_slice( - bcx.ccx().sess.str_of(field.ident))] - + self.c_mt(field.mt)); - } - } + let extra = ~[self.c_uint(fields.len())] + + self.c_size_and_align(t); + do self.bracketed(~"class", extra) |this| { + for fields.eachi |i, field| { + let extra = ~[this.c_uint(i), + this.c_slice( + bcx.ccx().sess.str_of(field.ident))] + + this.c_mt(field.mt); + this.visit(~"class_field", extra); + } + } } // FIXME (#2595): visiting all the variants in turn is probably @@ -267,20 +281,20 @@ pub impl Reflector { let tcx = bcx.ccx().tcx; let variants = ty::substd_enum_variants(tcx, did, substs); - do self.bracketed(~"enum", - ~[self.c_uint(vec::len(variants))] - + self.c_size_and_align(t)) { + let extra = ~[self.c_uint(vec::len(variants))] + + self.c_size_and_align(t); + do self.bracketed(~"enum", extra) |this| { for variants.eachi |i, v| { - do self.bracketed(~"enum_variant", - ~[self.c_uint(i), - self.c_int(v.disr_val), - self.c_uint(vec::len(v.args)), - self.c_slice( - bcx.ccx().sess.str_of(v.name))]) { + let extra1 = ~[this.c_uint(i), + this.c_int(v.disr_val), + this.c_uint(vec::len(v.args)), + this.c_slice( + bcx.ccx().sess.str_of(v.name))]; + do this.bracketed(~"enum_variant", extra1) |this| { for v.args.eachi |j, a| { - self.visit(~"enum_variant_field", - ~[self.c_uint(j), - self.c_tydesc(*a)]); + let extra = ~[this.c_uint(j), + this.c_tydesc(*a)]; + this.visit(~"enum_variant_field", extra); } } } @@ -291,13 +305,17 @@ pub impl Reflector { ty::ty_trait(_, _, _) => self.leaf(~"trait"), ty::ty_infer(_) => self.leaf(~"infer"), ty::ty_err => self.leaf(~"err"), - ty::ty_param(p) => self.visit(~"param", ~[self.c_uint(p.idx)]), + ty::ty_param(p) => { + let extra = ~[self.c_uint(p.idx)]; + self.visit(~"param", extra) + } ty::ty_self => self.leaf(~"self"), ty::ty_type => self.leaf(~"type"), ty::ty_opaque_box => self.leaf(~"opaque_box"), ty::ty_opaque_closure_ptr(ck) => { - let ckval = ast_sigil_constant(ck); - self.visit(~"closure_ptr", ~[self.c_uint(ckval)]) + let ckval = ast_sigil_constant(ck); + let extra = ~[self.c_uint(ckval)]; + self.visit(~"closure_ptr", extra) } } } @@ -312,14 +330,14 @@ pub impl Reflector { ast::by_copy => 5u } }; - self.visit(~"fn_input", - ~[self.c_uint(i), + let extra = ~[self.c_uint(i), self.c_uint(modeval), - self.c_tydesc(arg.ty)]); + self.c_tydesc(arg.ty)]; + self.visit(~"fn_input", extra); } - self.visit(~"fn_output", - ~[self.c_uint(retval), - self.c_tydesc(sig.output)]); + let extra = ~[self.c_uint(retval), + self.c_tydesc(sig.output)]; + self.visit(~"fn_output", extra); } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 4350c62af6f..d60360f5546 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1934,7 +1934,6 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents { } cache.insert(ty_id, TC_NONE); - debug!("computing contents of %s", ty_to_str(cx, ty)); let _i = indenter(); let mut result = match get(ty).sty { @@ -2085,8 +2084,6 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents { result = result + TC_BIG; } - debug!("result = %s", result.to_str()); - cache.insert(ty_id, result); return result; } diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/typeck/infer/lattice.rs index 4bf8a0bae86..709864c0d13 100644 --- a/src/librustc/middle/typeck/infer/lattice.rs +++ b/src/librustc/middle/typeck/infer/lattice.rs @@ -118,11 +118,10 @@ pub impl CombineFields { // A remains a subtype of B. Actually, there are other options, // but that's the route we choose to take. - self.infcx.unify(&node_a, &node_b, |new_root, new_rank| { - self.set_var_to_merged_bounds(new_root, - &a_bounds, &b_bounds, - new_rank) - }) + let (new_root, new_rank) = self.infcx.unify(&node_a, &node_b); + self.set_var_to_merged_bounds(new_root, + &a_bounds, &b_bounds, + new_rank) } /// make variable a subtype of T diff --git a/src/librustc/middle/typeck/infer/region_inference.rs b/src/librustc/middle/typeck/infer/region_inference.rs index 027b99cc421..230bfe693e0 100644 --- a/src/librustc/middle/typeck/infer/region_inference.rs +++ b/src/librustc/middle/typeck/infer/region_inference.rs @@ -832,7 +832,7 @@ pub impl RegionVarBindings { (re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => { self.combine_vars( self.lubs, a, b, span, - |old_r, new_r| self.make_subregion(span, old_r, new_r)) + |this, old_r, new_r| this.make_subregion(span, old_r, new_r)) } _ => { @@ -859,7 +859,7 @@ pub impl RegionVarBindings { (re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => { self.combine_vars( self.glbs, a, b, span, - |old_r, new_r| self.make_subregion(span, new_r, old_r)) + |this, old_r, new_r| this.make_subregion(span, new_r, old_r)) } _ => { @@ -915,7 +915,9 @@ pub impl RegionVarBindings { a: Region, b: Region, span: span, - relate: &fn(old_r: Region, new_r: Region) -> cres<()>) + relate: &fn(self: &mut RegionVarBindings, + old_r: Region, + new_r: Region) -> cres<()>) -> cres { let vars = TwoRegions { a: a, b: b }; match combines.find(&vars) { @@ -926,8 +928,8 @@ pub impl RegionVarBindings { if self.in_snapshot() { self.undo_log.push(AddCombination(combines, vars)); } - do relate(a, re_infer(ReVar(c))).then { - do relate(b, re_infer(ReVar(c))).then { + do relate(self, a, re_infer(ReVar(c))).then { + do relate(self, b, re_infer(ReVar(c))).then { debug!("combine_vars() c=%?", c); Ok(re_infer(ReVar(c))) } @@ -1035,7 +1037,8 @@ pub impl RegionVarBindings { */ fn resolve_regions(&mut self) { debug!("RegionVarBindings: resolve_regions()"); - self.values.put_back(self.infer_variable_values()); + let v = self.infer_variable_values(); + self.values.put_back(v); } } @@ -1220,7 +1223,7 @@ impl RegionVarBindings { let mut graph = self.construct_graph(); self.expansion(&mut graph); self.contraction(&mut graph); - self.extract_values_and_report_conflicts(&mut graph) + self.extract_values_and_report_conflicts(&graph) } fn construct_graph(&mut self) -> Graph { @@ -1257,14 +1260,14 @@ impl RegionVarBindings { for uint::range(0, num_edges) |edge_idx| { match graph.edges[edge_idx].constraint { - ConstrainVarSubVar(copy a_id, copy b_id) => { + ConstrainVarSubVar(a_id, b_id) => { insert_edge(&mut graph, a_id, Outgoing, edge_idx); insert_edge(&mut graph, b_id, Incoming, edge_idx); } - ConstrainRegSubVar(_, copy b_id) => { + ConstrainRegSubVar(_, b_id) => { insert_edge(&mut graph, b_id, Incoming, edge_idx); } - ConstrainVarSubReg(copy a_id, _) => { + ConstrainVarSubReg(a_id, _) => { insert_edge(&mut graph, a_id, Outgoing, edge_idx); } } @@ -1285,17 +1288,17 @@ impl RegionVarBindings { } fn expansion(&mut self, graph: &mut Graph) { - do self.iterate_until_fixed_point(~"Expansion", graph) |edge| { + do iterate_until_fixed_point(~"Expansion", graph) |nodes, edge| { match edge.constraint { - ConstrainRegSubVar(copy a_region, copy b_vid) => { - let b_node = &mut graph.nodes[*b_vid]; + ConstrainRegSubVar(a_region, b_vid) => { + let b_node = &mut nodes[*b_vid]; self.expand_node(a_region, b_vid, b_node) } - ConstrainVarSubVar(copy a_vid, copy b_vid) => { - match graph.nodes[*a_vid].value { + ConstrainVarSubVar(a_vid, b_vid) => { + match nodes[*a_vid].value { NoValue | ErrorValue => false, - Value(copy a_region) => { - let b_node = &mut graph.nodes[*b_vid]; + Value(a_region) => { + let b_node = &mut nodes[*b_vid]; self.expand_node(a_region, b_vid, b_node) } } @@ -1325,7 +1328,7 @@ impl RegionVarBindings { return true; } - Value(copy cur_region) => { + Value(cur_region) => { let lub = self.lub_concrete_regions(a_region, cur_region); if lub == cur_region { return false; @@ -1345,23 +1348,23 @@ impl RegionVarBindings { } fn contraction(&mut self, graph: &mut Graph) { - do self.iterate_until_fixed_point(~"Contraction", graph) |edge| { + do iterate_until_fixed_point(~"Contraction", graph) |nodes, edge| { match edge.constraint { ConstrainRegSubVar(*) => { // This is an expansion constraint. Ignore. false } - ConstrainVarSubVar(copy a_vid, copy b_vid) => { - match graph.nodes[*b_vid].value { + ConstrainVarSubVar(a_vid, b_vid) => { + match nodes[*b_vid].value { NoValue | ErrorValue => false, - Value(copy b_region) => { - let a_node = &mut graph.nodes[*a_vid]; + Value(b_region) => { + let a_node = &mut nodes[*a_vid]; self.contract_node(a_vid, a_node, b_region) } } } - ConstrainVarSubReg(copy a_vid, copy b_region) => { - let a_node = &mut graph.nodes[*a_vid]; + ConstrainVarSubReg(a_vid, b_region) => { + let a_node = &mut nodes[*a_vid]; self.contract_node(a_vid, a_node, b_region) } } @@ -1387,7 +1390,7 @@ impl RegionVarBindings { false // no change } - Value(copy a_region) => { + Value(a_region) => { match a_node.classification { Expanding => { check_node(self, a_vid, a_node, a_region, b_region) @@ -1438,29 +1441,10 @@ impl RegionVarBindings { } } - fn iterate_until_fixed_point(&mut self, - tag: ~str, - graph: &mut Graph, - body: &fn(edge: &GraphEdge) -> bool) { - let mut iteration = 0; - let mut changed = true; - let num_edges = graph.edges.len(); - while changed { - changed = false; - iteration += 1; - debug!("---- %s Iteration #%u", tag, iteration); - for uint::range(0, num_edges) |edge_idx| { - changed |= body(&graph.edges[edge_idx]); - debug!(" >> Change after edge #%?: %?", - edge_idx, graph.edges[edge_idx]); - } - } - debug!("---- %s Complete after %u iteration(s)", tag, iteration); - } - - fn extract_values_and_report_conflicts(&mut self, - graph: &mut Graph) - -> ~[GraphNodeValue] { + fn extract_values_and_report_conflicts( + &mut self, + graph: &Graph) -> ~[GraphNodeValue] + { let dup_map = TwoRegionsMap(); graph.nodes.mapi(|idx, node| { match node.value { @@ -1525,7 +1509,7 @@ impl RegionVarBindings { } fn report_error_for_expanding_node(&mut self, - graph: &mut Graph, + graph: &Graph, dup_map: TwoRegionsMap, node_idx: RegionVid) { // Errors in expanding nodes result from a lower-bound that is @@ -1578,7 +1562,7 @@ impl RegionVarBindings { } fn report_error_for_contracting_node(&mut self, - graph: &mut Graph, + graph: &Graph, dup_map: TwoRegionsMap, node_idx: RegionVid) { // Errors in contracting nodes result from two upper-bounds @@ -1632,7 +1616,7 @@ impl RegionVarBindings { } fn collect_concrete_regions(&mut self, - graph: &mut Graph, + graph: &Graph, orig_node_idx: RegionVid, dir: Direction) -> ~[SpannedRegion] { @@ -1676,7 +1660,7 @@ impl RegionVarBindings { } fn each_edge(&mut self, - graph: &mut Graph, + graph: &Graph, node_idx: RegionVid, dir: Direction, op: fn(edge: &GraphEdge) -> bool) { @@ -1690,3 +1674,25 @@ impl RegionVarBindings { } } } + +fn iterate_until_fixed_point( + tag: ~str, + graph: &mut Graph, + body: &fn(nodes: &mut [GraphNode], edge: &GraphEdge) -> bool) +{ + let mut iteration = 0; + let mut changed = true; + let num_edges = graph.edges.len(); + while changed { + changed = false; + iteration += 1; + debug!("---- %s Iteration #%u", tag, iteration); + for uint::range(0, num_edges) |edge_idx| { + changed |= body(graph.nodes, &graph.edges[edge_idx]); + debug!(" >> Change after edge #%?: %?", + edge_idx, graph.edges[edge_idx]); + } + } + debug!("---- %s Complete after %u iteration(s)", tag, iteration); +} + diff --git a/src/librustc/middle/typeck/infer/unify.rs b/src/librustc/middle/typeck/infer/unify.rs index 7668388e323..69e4f373f97 100644 --- a/src/librustc/middle/typeck/infer/unify.rs +++ b/src/librustc/middle/typeck/infer/unify.rs @@ -43,9 +43,10 @@ pub trait UnifyVid { } pub impl InferCtxt { - fn get>(&mut self, - +vid: V) - -> Node { + fn get>( + &mut self, + +vid: V) -> Node + { /*! * * Find the root node for `vid`. This uses the standard @@ -53,27 +54,38 @@ pub impl InferCtxt { * http://en.wikipedia.org/wiki/Disjoint-set_data_structure */ + let tcx = self.tcx; let vb = UnifyVid::appropriate_vals_and_bindings(self); - let vid_u = vid.to_uint(); - match vb.vals.find(vid_u) { - None => { - self.tcx.sess.bug(fmt!("failed lookup of vid `%u`", vid_u)); - } - Some(ref var_val) => { - match (*var_val) { - Redirect(vid) => { - let node: Node = self.get(vid); - if node.root != vid { - // Path compression - vb.vals.insert(vid.to_uint(), Redirect(node.root)); + return helper(tcx, vb, vid); + + fn helper( + tcx: ty::ctxt, + vb: &mut ValsAndBindings, + vid: V) -> Node + { + let vid_u = vid.to_uint(); + match vb.vals.find(vid_u) { + None => { + tcx.sess.bug(fmt!( + "failed lookup of vid `%u`", vid_u)); + } + Some(ref var_val) => { + match *var_val { + Redirect(vid) => { + let node: Node = helper(tcx, vb, vid); + if node.root != vid { + // Path compression + vb.vals.insert(vid.to_uint(), + Redirect(node.root)); + } + node + } + Root(ref pt, rk) => { + Node {root: vid, possible_types: *pt, rank: rk} + } + } } - node - } - Root(ref pt, rk) => { - Node {root: vid, possible_types: *pt, rank: rk} - } } - } } } @@ -86,21 +98,22 @@ pub impl InferCtxt { * Sets the value for `vid` to `new_v`. `vid` MUST be a root node! */ - let vb = UnifyVid::appropriate_vals_and_bindings(self); - let old_v = vb.vals.get(vid.to_uint()); - vb.bindings.push((vid, old_v)); - vb.vals.insert(vid.to_uint(), new_v); + debug!("Updating variable %s to %s", + vid.to_str(), new_v.inf_str(self)); - debug!("Updating variable %s from %s to %s", - vid.to_str(), old_v.inf_str(self), new_v.inf_str(self)); + { // FIXME(#4903)---borrow checker is not flow sensitive + let vb = UnifyVid::appropriate_vals_and_bindings(self); + let old_v = vb.vals.get(vid.to_uint()); + vb.bindings.push((vid, old_v)); + vb.vals.insert(vid.to_uint(), new_v); + } } - fn unify, R>( - &mut self, - node_a: &Node, - node_b: &Node, - op: &fn(new_root: V, new_rank: uint) -> R - ) -> R { + fn unify>( + &mut self, + node_a: &Node, + node_b: &Node) -> (V, uint) + { // Rank optimization: if you don't know what it is, check // out @@ -113,17 +126,17 @@ pub impl InferCtxt { // a has greater rank, so a should become b's parent, // i.e., b should redirect to a. self.set(node_b.root, Redirect(node_a.root)); - op(node_a.root, node_a.rank) + (node_a.root, node_a.rank) } else if node_a.rank < node_b.rank { // b has greater rank, so a should redirect to b. self.set(node_a.root, Redirect(node_b.root)); - op(node_b.root, node_b.rank) + (node_b.root, node_b.rank) } else { // If equal, redirect one to the other and increment the // other's rank. assert node_a.rank == node_b.rank; self.set(node_b.root, Redirect(node_a.root)); - op(node_a.root, node_a.rank + 1) + (node_a.root, node_a.rank + 1) } } @@ -183,9 +196,8 @@ pub impl InferCtxt { } }; - self.unify(&node_a, &node_b, |new_root, new_rank| { - self.set(new_root, Root(combined, new_rank)); - }); + let (new_root, new_rank) = self.unify(&node_a, &node_b); + self.set(new_root, Root(combined, new_rank)); return uok(); } diff --git a/src/libstd/arc.rs b/src/libstd/arc.rs index a1fd7a66f7e..ff28d2cbebf 100644 --- a/src/libstd/arc.rs +++ b/src/libstd/arc.rs @@ -184,25 +184,30 @@ impl &MutexARC { */ #[inline(always)] unsafe fn access(blk: fn(x: &mut T) -> U) -> U { - let state = unsafe { get_shared_mutable_state(&self.x) }; - // Borrowck would complain about this if the function were not already - // unsafe. See borrow_rwlock, far below. - do (&state.lock).lock { - check_poison(true, state.failed); - let _z = PoisonOnFail(&mut state.failed); - blk(&mut state.data) + unsafe { + let state = get_shared_mutable_state(&self.x); + // Borrowck would complain about this if the function were + // not already unsafe. See borrow_rwlock, far below. + do (&(*state).lock).lock { + check_poison(true, (*state).failed); + let _z = PoisonOnFail(&mut (*state).failed); + blk(&mut (*state).data) + } } } /// As access(), but with a condvar, as sync::mutex.lock_cond(). #[inline(always)] unsafe fn access_cond(blk: fn(x: &x/mut T, c: &c/Condvar) -> U) -> U { - let state = unsafe { get_shared_mutable_state(&self.x) }; - do (&state.lock).lock_cond |cond| { - check_poison(true, state.failed); - let _z = PoisonOnFail(&mut state.failed); - blk(&mut state.data, - &Condvar { is_mutex: true, failed: &mut state.failed, - cond: cond }) + unsafe { + let state = get_shared_mutable_state(&self.x); + do (&(*state).lock).lock_cond |cond| { + check_poison(true, (*state).failed); + let _z = PoisonOnFail(&mut (*state).failed); + blk(&mut (*state).data, + &Condvar {is_mutex: true, + failed: &mut (*state).failed, + cond: cond }) + } } } } @@ -285,8 +290,10 @@ pub fn RWARC(user_data: T) -> RWARC { * Create a reader/writer ARC with the supplied data and a specified number * of condvars (as sync::rwlock_with_condvars). */ -pub fn rw_arc_with_condvars(user_data: T, - num_condvars: uint) -> RWARC { +pub fn rw_arc_with_condvars( + user_data: T, + num_condvars: uint) -> RWARC +{ let data = RWARCInner { lock: rwlock_with_condvars(num_condvars), failed: false, data: move user_data }; @@ -315,23 +322,28 @@ impl &RWARC { */ #[inline(always)] fn write(blk: fn(x: &mut T) -> U) -> U { - let state = unsafe { get_shared_mutable_state(&self.x) }; - do borrow_rwlock(state).write { - check_poison(false, state.failed); - let _z = PoisonOnFail(&mut state.failed); - blk(&mut state.data) + unsafe { + let state = get_shared_mutable_state(&self.x); + do (*borrow_rwlock(state)).write { + check_poison(false, (*state).failed); + let _z = PoisonOnFail(&mut (*state).failed); + blk(&mut (*state).data) + } } } /// As write(), but with a condvar, as sync::rwlock.write_cond(). #[inline(always)] fn write_cond(blk: fn(x: &x/mut T, c: &c/Condvar) -> U) -> U { - let state = unsafe { get_shared_mutable_state(&self.x) }; - do borrow_rwlock(state).write_cond |cond| { - check_poison(false, state.failed); - let _z = PoisonOnFail(&mut state.failed); - blk(&mut state.data, - &Condvar { is_mutex: false, failed: &mut state.failed, - cond: cond }) + unsafe { + let state = get_shared_mutable_state(&self.x); + do (*borrow_rwlock(state)).write_cond |cond| { + check_poison(false, (*state).failed); + let _z = PoisonOnFail(&mut (*state).failed); + blk(&mut (*state).data, + &Condvar {is_mutex: false, + failed: &mut (*state).failed, + cond: cond}) + } } } /** @@ -369,11 +381,14 @@ impl &RWARC { * ~~~ */ fn write_downgrade(blk: fn(v: RWWriteMode) -> U) -> U { - let state = unsafe { get_shared_mutable_state(&self.x) }; - do borrow_rwlock(state).write_downgrade |write_mode| { - check_poison(false, state.failed); - blk(RWWriteMode((&mut state.data, move write_mode, - PoisonOnFail(&mut state.failed)))) + unsafe { + let state = get_shared_mutable_state(&self.x); + do (*borrow_rwlock(state)).write_downgrade |write_mode| { + check_poison(false, (*state).failed); + blk(RWWriteMode((&mut (*state).data, + move write_mode, + PoisonOnFail(&mut (*state).failed)))) + } } } @@ -417,8 +432,8 @@ pub fn unwrap_rw_arc(arc: RWARC) -> T { // lock it. This wraps the unsafety, with the justification that the 'lock' // field is never overwritten; only 'failed' and 'data'. #[doc(hidden)] -fn borrow_rwlock(state: &r/mut RWARCInner) -> &r/RWlock { - unsafe { cast::transmute(&mut state.lock) } +fn borrow_rwlock(state: *const RWARCInner) -> *RWlock { + unsafe { cast::transmute(&const (*state).lock) } } // FIXME (#3154) ice with struct/& prevents these from being structs. diff --git a/src/libstd/priority_queue.rs b/src/libstd/priority_queue.rs index 5248ab1742e..a64aa5e9687 100644 --- a/src/libstd/priority_queue.rs +++ b/src/libstd/priority_queue.rs @@ -81,7 +81,8 @@ impl PriorityQueue { /// Push an item onto the queue fn push(&mut self, item: T) { self.data.push(item); - self.siftup(0, self.len() - 1); + let new_len = self.len() - 1; + self.siftup(0, new_len); } /// Optimized version of a push followed by a pop @@ -179,7 +180,8 @@ impl PriorityQueue { } priv fn siftdown(&mut self, pos: uint) { - self.siftdown_range(pos, self.len()); + let len = self.len(); + self.siftdown_range(pos, len); } } diff --git a/src/libstd/smallintmap.rs b/src/libstd/smallintmap.rs index 9642dd0c3dd..2e5cd8956cd 100644 --- a/src/libstd/smallintmap.rs +++ b/src/libstd/smallintmap.rs @@ -134,10 +134,11 @@ pub impl SmallIntMap { pub impl SmallIntMap { fn update_with_key(&mut self, key: uint, val: V, ff: fn(uint, V, V) -> V) -> bool { - match self.find(&key) { - None => self.insert(key, val), - Some(orig) => self.insert(key, ff(key, copy *orig, val)), - } + let new_val = match self.find(&key) { + None => val, + Some(orig) => ff(key, *orig, val) + }; + self.insert(key, new_val) } fn update(&mut self, key: uint, newval: V, ff: fn(V, V) -> V) -> bool { diff --git a/src/libstd/treemap.rs b/src/libstd/treemap.rs index 26bf232adf5..2fdaeb545a2 100644 --- a/src/libstd/treemap.rs +++ b/src/libstd/treemap.rs @@ -673,45 +673,45 @@ fn remove(node: &mut Option<~TreeNode>, key: &K) -> bool { } }; - if this { - *node = None; - return true; - } + if !this { + let left_level = save.left.map_default(0, |x| x.level); + let right_level = save.right.map_default(0, |x| x.level); - let left_level = save.left.map_default(0, |x| x.level); - let right_level = save.right.map_default(0, |x| x.level); + // re-balance, if necessary + if left_level < save.level - 1 || right_level < save.level - 1 { + save.level -= 1; - // re-balance, if necessary - if left_level < save.level - 1 || right_level < save.level - 1 { - save.level -= 1; - - if right_level > save.level { - do save.right.mutate |mut x| { x.level = save.level; x } - } - - skew(save); - - match save.right { - Some(ref mut right) => { - skew(right); - match right.right { - Some(ref mut x) => { skew(x) }, - None => () + if right_level > save.level { + do save.right.mutate |mut x| { x.level = save.level; x } + } + + skew(save); + + match save.right { + Some(ref mut right) => { + skew(right); + match right.right { + Some(ref mut x) => { skew(x) }, + None => () + } + } + None => () + } + + split(save); + match save.right { + Some(ref mut x) => { split(x) }, + None => () } - } - None => () } - split(save); - match save.right { - Some(ref mut x) => { split(x) }, - None => () - } + return removed; } - - removed } } + + *node = None; + return true; } #[cfg(test)] diff --git a/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs b/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs new file mode 100644 index 00000000000..47b6b4de642 --- /dev/null +++ b/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs @@ -0,0 +1,124 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo { + bar1: Bar, + bar2: Bar +} + +struct Bar { + int1: int, + int2: int, +} + +fn make_foo() -> ~Foo { die!() } + +fn borrow_same_field_twice_mut_mut() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1; + let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_same_field_twice_mut_imm() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1; + let _bar2 = &foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_same_field_twice_imm_mut() { + let mut foo = make_foo(); + let _bar1 = &foo.bar1; + let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_same_field_twice_imm_imm() { + let mut foo = make_foo(); + let _bar1 = &foo.bar1; + let _bar2 = &foo.bar1; +} + +fn borrow_both_mut() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1; + let _bar2 = &mut foo.bar2; +} + +fn borrow_both_mut_pattern() { + let mut foo = make_foo(); + match *foo { + Foo { bar1: ref mut _bar1, bar2: ref mut _bar2 } => {} + } +} + +fn borrow_var_and_pattern() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1; + match *foo { + Foo { bar1: ref mut _bar1, bar2: _ } => {} + //~^ ERROR conflicts with prior loan + } +} + +fn borrow_mut_and_base_imm() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1.int1; + let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan + let _foo2 = &*foo; //~ ERROR conflicts with prior loan +} + +fn borrow_mut_and_base_mut() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1.int1; + let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_mut_and_base_mut2() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1.int1; + let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan +} + +fn borrow_imm_and_base_mut() { + let mut foo = make_foo(); + let _bar1 = &foo.bar1.int1; + let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_imm_and_base_mut2() { + let mut foo = make_foo(); + let _bar1 = &foo.bar1.int1; + let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan +} + +fn borrow_imm_and_base_imm() { + let mut foo = make_foo(); + let _bar1 = &foo.bar1.int1; + let _foo1 = &foo.bar1; + let _foo2 = &*foo; +} + +fn borrow_mut_and_imm() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1; + let _foo1 = &foo.bar2; +} + +fn borrow_mut_from_imm() { + let foo = make_foo(); + let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow +} + +fn borrow_long_path_both_mut() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1.int1; + let _foo1 = &mut foo.bar2.int2; +} + +fn main() {} diff --git a/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs b/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs new file mode 100644 index 00000000000..30757cc6e77 --- /dev/null +++ b/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs @@ -0,0 +1,124 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo { + bar1: Bar, + bar2: Bar +} + +struct Bar { + int1: int, + int2: int, +} + +fn make_foo() -> Foo { die!() } + +fn borrow_same_field_twice_mut_mut() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1; + let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_same_field_twice_mut_imm() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1; + let _bar2 = &foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_same_field_twice_imm_mut() { + let mut foo = make_foo(); + let _bar1 = &foo.bar1; + let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_same_field_twice_imm_imm() { + let mut foo = make_foo(); + let _bar1 = &foo.bar1; + let _bar2 = &foo.bar1; +} + +fn borrow_both_mut() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1; + let _bar2 = &mut foo.bar2; +} + +fn borrow_both_mut_pattern() { + let mut foo = make_foo(); + match foo { + Foo { bar1: ref mut _bar1, bar2: ref mut _bar2 } => {} + } +} + +fn borrow_var_and_pattern() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1; + match foo { + Foo { bar1: ref mut _bar1, bar2: _ } => {} + //~^ ERROR conflicts with prior loan + } +} + +fn borrow_mut_and_base_imm() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1.int1; + let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan + let _foo2 = &foo; //~ ERROR conflicts with prior loan +} + +fn borrow_mut_and_base_mut() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1.int1; + let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_mut_and_base_mut2() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1.int1; + let _foo2 = &mut foo; //~ ERROR conflicts with prior loan +} + +fn borrow_imm_and_base_mut() { + let mut foo = make_foo(); + let _bar1 = &foo.bar1.int1; + let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_imm_and_base_mut2() { + let mut foo = make_foo(); + let _bar1 = &foo.bar1.int1; + let _foo2 = &mut foo; //~ ERROR conflicts with prior loan +} + +fn borrow_imm_and_base_imm() { + let mut foo = make_foo(); + let _bar1 = &foo.bar1.int1; + let _foo1 = &foo.bar1; + let _foo2 = &foo; +} + +fn borrow_mut_and_imm() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1; + let _foo1 = &foo.bar2; +} + +fn borrow_mut_from_imm() { + let foo = make_foo(); + let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow +} + +fn borrow_long_path_both_mut() { + let mut foo = make_foo(); + let _bar1 = &mut foo.bar1.int1; + let _foo1 = &mut foo.bar2.int2; +} + +fn main() {} diff --git a/src/test/compile-fail/borrowck-confuse-region.rs b/src/test/compile-fail/borrowck-confuse-region.rs deleted file mode 100644 index 7b5d9829f5f..00000000000 --- a/src/test/compile-fail/borrowck-confuse-region.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Here we are checking that a reasonable error msg is provided. -// -// The current message is not ideal, but we used to say "borrowed -// pointer has lifetime &, but the borrowed value only has lifetime &" -// which is definitely no good. - - -fn get() -> &int { - //~^ NOTE borrowed pointer must be valid for the anonymous lifetime #1 defined on - //~^^ NOTE ...but borrowed value is only valid for the block at - let x = 3; - return &x; - //~^ ERROR illegal borrow -} - -fn main() {} diff --git a/src/test/compile-fail/borrowck-imm-field-mut-base.rs b/src/test/compile-fail/borrowck-imm-field-mut-base.rs deleted file mode 100644 index 5c3fe229602..00000000000 --- a/src/test/compile-fail/borrowck-imm-field-mut-base.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -struct Foo { - mut x: uint -} - -struct Bar { - foo: Foo -} - -fn main() { - let mut b = Bar { foo: Foo { x: 3 } }; - let p = &b.foo.x; - let q = &mut b.foo; //~ ERROR loan of mutable field as mutable conflicts with prior loan - //~^ ERROR loan of mutable local variable as mutable conflicts with prior loan - let r = &mut b; //~ ERROR loan of mutable local variable as mutable conflicts with prior loan - //~^ ERROR loan of mutable local variable as mutable conflicts with prior loan - io::println(fmt!("*p = %u", *p)); - q.x += 1; - r.foo.x += 1; - io::println(fmt!("*p = %u", *p)); -} diff --git a/src/test/compile-fail/borrowck-imm-field-imm-base.rs b/src/test/compile-fail/borrowck-insert-during-each.rs similarity index 57% rename from src/test/compile-fail/borrowck-imm-field-imm-base.rs rename to src/test/compile-fail/borrowck-insert-during-each.rs index 0d850fd5cee..1dcf8268440 100644 --- a/src/test/compile-fail/borrowck-imm-field-imm-base.rs +++ b/src/test/compile-fail/borrowck-insert-during-each.rs @@ -8,20 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use core::hashmap::linear::LinearSet; + struct Foo { - x: uint + n: LinearSet, } -struct Bar { - foo: Foo +impl Foo { + fn foo(&mut self, fun: fn(&int)) { + for self.n.each |f| { + fun(f); + } + } +} + +fn bar(f: &mut Foo) { + do f.foo |a| { //~ NOTE prior loan as mutable granted here + f.n.insert(*a); //~ ERROR conflicts with prior loan + } } fn main() { - let mut b = Bar { foo: Foo { x: 3 } }; - let p = &b; //~ NOTE prior loan as immutable granted here - let q = &mut b.foo.x; //~ ERROR loan of mutable local variable as mutable conflicts with prior loan - let r = &p.foo.x; - io::println(fmt!("*r = %u", *r)); - *q += 1; - io::println(fmt!("*r = %u", *r)); + let mut f = Foo { n: LinearSet::new() }; + bar(&mut f); } \ No newline at end of file diff --git a/src/test/compile-fail/borrowck-reborrow-from-mut.rs b/src/test/compile-fail/borrowck-reborrow-from-mut.rs new file mode 100644 index 00000000000..60f817dee0c --- /dev/null +++ b/src/test/compile-fail/borrowck-reborrow-from-mut.rs @@ -0,0 +1,106 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo { + bar1: Bar, + bar2: Bar +} + +struct Bar { + int1: int, + int2: int, +} + +fn borrow_same_field_twice_mut_mut(foo: &mut Foo) { + let _bar1 = &mut foo.bar1; + let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_same_field_twice_mut_imm(foo: &mut Foo) { + let _bar1 = &mut foo.bar1; + let _bar2 = &foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_same_field_twice_imm_mut(foo: &mut Foo) { + let _bar1 = &foo.bar1; + let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_same_field_twice_imm_imm(foo: &mut Foo) { + let _bar1 = &foo.bar1; + let _bar2 = &foo.bar1; +} + +fn borrow_both_mut(foo: &mut Foo) { + let _bar1 = &mut foo.bar1; + let _bar2 = &mut foo.bar2; +} + +fn borrow_both_mut_pattern(foo: &mut Foo) { + match *foo { + Foo { bar1: ref mut _bar1, bar2: ref mut _bar2 } => {} + } +} + +fn borrow_var_and_pattern(foo: &mut Foo) { + let _bar1 = &mut foo.bar1; + match *foo { + Foo { bar1: ref mut _bar1, bar2: _ } => {} + //~^ ERROR conflicts with prior loan + } +} + +fn borrow_mut_and_base_imm(foo: &mut Foo) { + let _bar1 = &mut foo.bar1.int1; + let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan + let _foo2 = &*foo; //~ ERROR conflicts with prior loan +} + +fn borrow_mut_and_base_mut(foo: &mut Foo) { + let _bar1 = &mut foo.bar1.int1; + let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_mut_and_base_mut2(foo: &mut Foo) { + let _bar1 = &mut foo.bar1.int1; + let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan +} + +fn borrow_imm_and_base_mut(foo: &mut Foo) { + let _bar1 = &foo.bar1.int1; + let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan +} + +fn borrow_imm_and_base_mut2(foo: &mut Foo) { + let _bar1 = &foo.bar1.int1; + let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan +} + +fn borrow_imm_and_base_imm(foo: &mut Foo) { + let _bar1 = &foo.bar1.int1; + let _foo1 = &foo.bar1; + let _foo2 = &*foo; +} + +fn borrow_mut_and_imm(foo: &mut Foo) { + let _bar1 = &mut foo.bar1; + let _foo1 = &foo.bar2; +} + +fn borrow_mut_from_imm(foo: &Foo) { + let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow +} + +fn borrow_long_path_both_mut(foo: &mut Foo) { + let _bar1 = &mut foo.bar1.int1; + let _foo1 = &mut foo.bar2.int2; +} + +fn main() {}