1
Fork 0

Adjust borrow checker algorithm to address #4856 unsoundness,

and then adjust code to match. rs=unsound (will review post-landing)
This commit is contained in:
Niko Matsakis 2013-02-08 22:21:45 -08:00
parent 91c59f5c9a
commit ab2534974c
25 changed files with 1046 additions and 575 deletions

View file

@ -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, /// 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)) { fn consume(&mut self, f: fn(K, V)) {

View file

@ -256,15 +256,15 @@ pub unsafe fn shared_mutable_state<T: Owned>(data: T) ->
} }
#[inline(always)] #[inline(always)]
pub unsafe fn get_shared_mutable_state<T: Owned>(rc: &a/SharedMutableState<T>) pub unsafe fn get_shared_mutable_state<T: Owned>(
-> &a/mut T { rc: *SharedMutableState<T>) -> *mut T
{
unsafe { unsafe {
let ptr: ~ArcData<T> = cast::reinterpret_cast(&(*rc).data); let ptr: ~ArcData<T> = cast::reinterpret_cast(&(*rc).data);
assert ptr.count > 0; assert ptr.count > 0;
// Cast us back into the correct region let r = cast::transmute(option::get_ref(&ptr.data));
let r = cast::transmute_region(option::get_ref(&ptr.data));
cast::forget(move ptr); cast::forget(move ptr);
return cast::transmute_mut(r); return r;
} }
} }
#[inline(always)] #[inline(always)]
@ -376,15 +376,17 @@ impl<T: Owned> Exclusive<T> {
// the exclusive. Supporting that is a work in progress. // the exclusive. Supporting that is a work in progress.
#[inline(always)] #[inline(always)]
unsafe fn with<U>(f: fn(x: &mut T) -> U) -> U { unsafe fn with<U>(f: fn(x: &mut T) -> U) -> U {
let rec = unsafe { get_shared_mutable_state(&self.x) }; unsafe {
do rec.lock.lock { let rec = get_shared_mutable_state(&self.x);
if rec.failed { do (*rec).lock.lock {
die!(~"Poisoned exclusive - another task failed inside!"); 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
} }
} }

View file

@ -2071,17 +2071,19 @@ pub mod raw {
/// Appends a byte to a string. (Not UTF-8 safe). /// Appends a byte to a string. (Not UTF-8 safe).
pub unsafe fn push_byte(s: &mut ~str, b: u8) { 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| { do as_buf(*s) |buf, len| {
let buf: *mut u8 = ::cast::reinterpret_cast(&buf); let buf: *mut u8 = ::cast::reinterpret_cast(&buf);
*ptr::mut_offset(buf, len) = b; *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). /// Appends a vector of bytes to a string. (Not UTF-8 safe).
unsafe fn push_bytes(s: &mut ~str, bytes: &[u8]) { 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); } for vec::each(bytes) |byte| { push_byte(&mut *s, *byte); }
} }

View file

@ -623,13 +623,15 @@ unsafe fn push_fast<T>(v: &mut ~[T], initval: T) {
#[inline(never)] #[inline(never)]
fn push_slow<T>(v: &mut ~[T], initval: T) { fn push_slow<T>(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) } unsafe { push_fast(v, initval) }
} }
#[inline(always)] #[inline(always)]
pub fn push_all<T: Copy>(v: &mut ~[T], rhs: &[const T]) { pub fn push_all<T: Copy>(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| { for uint::range(0u, rhs.len()) |i| {
push(&mut *v, unsafe { raw::get(rhs, i) }) push(&mut *v, unsafe { raw::get(rhs, i) })
@ -638,7 +640,8 @@ pub fn push_all<T: Copy>(v: &mut ~[T], rhs: &[const T]) {
#[inline(always)] #[inline(always)]
pub fn push_all_move<T>(v: &mut ~[T], mut rhs: ~[T]) { pub fn push_all_move<T>(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 { unsafe {
do as_mut_buf(rhs) |p, len| { do as_mut_buf(rhs) |p, len| {
for uint::range(0, len) |i| { for uint::range(0, len) |i| {
@ -663,9 +666,9 @@ pub fn truncate<T>(v: &mut ~[T], newlen: uint) {
let mut dropped = rusti::init(); let mut dropped = rusti::init();
dropped <-> *ptr::mut_offset(p, i); 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<T: Copy>(lhs: ~[mut T], rhs: &[const T]) -> ~[mut T] {
* * initval - The value for the new elements * * initval - The value for the new elements
*/ */
pub fn grow<T: Copy>(v: &mut ~[T], n: uint, initval: &T) { pub fn grow<T: Copy>(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; let mut i: uint = 0u;
while i < n { while i < n {
@ -763,7 +767,8 @@ pub fn grow<T: Copy>(v: &mut ~[T], n: uint, initval: &T) {
* value * value
*/ */
pub fn grow_fn<T>(v: &mut ~[T], n: uint, op: iter::InitOp<T>) { pub fn grow_fn<T>(v: &mut ~[T], n: uint, op: iter::InitOp<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; let mut i: uint = 0u;
while i < n { while i < n {
v.push(op(i)); v.push(op(i));

View file

@ -305,22 +305,31 @@ impl CheckLoanCtxt {
return; return;
} }
match (old_loan.mutbl, new_loan.mutbl) { match (old_loan.kind, new_loan.kind) {
(m_const, _) | (_, m_const) | (m_imm, m_imm) => { (PartialFreeze, PartialTake) | (PartialTake, PartialFreeze) |
/*ok*/ (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( self.bccx.span_err(
new_loan.cmt.span, new_loan.cmt.span,
fmt!("loan of %s as %s \ fmt!("loan of %s as %s \
conflicts with prior loan", conflicts with prior loan",
self.bccx.cmt_to_str(new_loan.cmt), 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( self.bccx.span_note(
old_loan.cmt.span, old_loan.cmt.span,
fmt!("prior loan as %s granted here", 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 // are only assigned once
} else { } else {
match cmt.mutbl { match cmt.mutbl {
m_mutbl => { /*ok*/ } McDeclared | McInherited => { /*ok*/ }
m_const | m_imm => { McReadOnly | McImmutable => {
self.bccx.span_err( self.bccx.span_err(
ex.span, ex.span,
at.ing_form(self.bccx.cmt_to_str(cmt))); at.ing_form(self.bccx.cmt_to_str(cmt)));
return; return;
} }
} }
} }
@ -428,19 +437,20 @@ impl CheckLoanCtxt {
cmt: cmt, cmt: cmt,
lp: @loan_path) { lp: @loan_path) {
for self.walk_loans_of(ex.id, lp) |loan| { for self.walk_loans_of(ex.id, lp) |loan| {
match loan.mutbl { match loan.kind {
m_const => { /*ok*/ } Immobile => { /* ok */ }
m_mutbl | m_imm => { TotalFreeze | PartialFreeze |
self.bccx.span_err( TotalTake | PartialTake => {
ex.span, self.bccx.span_err(
fmt!("%s prohibited due to outstanding loan", ex.span,
at.ing_form(self.bccx.cmt_to_str(cmt)))); fmt!("%s prohibited due to outstanding loan",
self.bccx.span_note( at.ing_form(self.bccx.cmt_to_str(cmt))));
loan.cmt.span, self.bccx.span_note(
fmt!("loan of %s granted here", loan.cmt.span,
self.bccx.cmt_to_str(loan.cmt))); fmt!("loan of %s granted here",
return; self.bccx.cmt_to_str(loan.cmt)));
} return;
}
} }
} }

View file

@ -20,7 +20,10 @@ use core::prelude::*;
use middle::borrowck::preserve::{PreserveCondition, PcOk, PcIfPure}; use middle::borrowck::preserve::{PreserveCondition, PcOk, PcIfPure};
use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl}; 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::{req_maps};
use middle::borrowck::loan;
use middle::mem_categorization::{cat_binding, cat_discr, cmt, comp_variant}; use middle::mem_categorization::{cat_binding, cat_discr, cmt, comp_variant};
use middle::mem_categorization::{mem_categorization_ctxt}; use middle::mem_categorization::{mem_categorization_ctxt};
use middle::mem_categorization::{opt_deref_kind}; use middle::mem_categorization::{opt_deref_kind};
@ -340,13 +343,22 @@ impl GatherLoanCtxt {
fn guarantee_valid(@mut self, fn guarantee_valid(@mut self,
cmt: cmt, cmt: cmt,
req_mutbl: ast::mutability, 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; 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.cmt_to_repr(cmt),
self.bccx.mut_to_str(req_mutbl), req_mutbl,
loan_kind,
region_to_str(self.tcx(), scope_r)); region_to_str(self.tcx(), scope_r));
let _i = indenter(); let _i = indenter();
@ -362,10 +374,10 @@ impl GatherLoanCtxt {
// it within that scope, the loan will be detected and an // it within that scope, the loan will be detected and an
// error will be reported. // error will be reported.
Some(_) => { 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)); } Err(ref e) => { self.bccx.report((*e)); }
Ok(move loans) => { 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) // pointer is desired, that is ok as long as we are pure)
None => { None => {
let result: bckres<PreserveCondition> = { let result: bckres<PreserveCondition> = {
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, do self.bccx.preserve(cmt, scope_r,
self.item_ub, self.item_ub,
self.root_ub).chain |pc2| { self.root_ub).chain |pc2| {
@ -446,37 +458,41 @@ impl GatherLoanCtxt {
// reqires an immutable pointer, but `f` lives in (aliased) // reqires an immutable pointer, but `f` lives in (aliased)
// mutable memory. // mutable memory.
fn check_mutbl(@mut self, fn check_mutbl(@mut self,
req_mutbl: ast::mutability, loan_kind: LoanKind,
cmt: cmt) cmt: cmt)
-> bckres<PreserveCondition> { -> bckres<PreserveCondition> {
debug!("check_mutbl(req_mutbl=%?, cmt.mutbl=%?)", debug!("check_mutbl(loan_kind=%?, cmt.mutbl=%?)",
req_mutbl, cmt.mutbl); loan_kind, cmt.mutbl);
if req_mutbl == m_const || req_mutbl == cmt.mutbl { match loan_kind {
debug!("required is const or they are the same"); Immobile => Ok(PcOk),
Ok(PcOk)
} else { TotalTake | PartialTake => {
let e = bckerr { cmt: cmt, code: err_mutbl(req_mutbl) }; if cmt.mutbl.is_mutable() {
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() {
Ok(PcOk) Ok(PcOk)
} else { } else {
// you can treat mutable things as imm if you are pure Err(bckerr { cmt: cmt, code: err_mutbl(loan_kind) })
debug!("imm required, must be pure"); }
}
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)) Ok(PcIfPure(e))
} }
} else {
Err(e)
} }
} }
} }
fn add_loans(@mut self, fn add_loans(@mut self,
cmt: cmt, cmt: cmt,
req_mutbl: ast::mutability, loan_kind: LoanKind,
scope_r: ty::Region, scope_r: ty::Region,
+loans: ~[Loan]) { +loans: ~[Loan]) {
if loans.len() == 0 { if loans.len() == 0 {
@ -526,7 +542,7 @@ impl GatherLoanCtxt {
self.add_loans_to_scope_id(scope_id, move loans); 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; self.bccx.stats.loaned_paths_imm += 1;
if self.tcx().sess.borrowck_note_loan() { if self.tcx().sess.borrowck_note_loan() {
@ -542,7 +558,9 @@ impl GatherLoanCtxt {
fn add_loans_to_scope_id(@mut self, fn add_loans_to_scope_id(@mut self,
scope_id: ast::node_id, scope_id: ast::node_id,
+loans: ~[Loan]) { +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) { match self.req_maps.req_loan_map.find(&scope_id) {
Some(req_loans) => { Some(req_loans) => {
req_loans.push_all(loans); req_loans.push_all(loans);

View file

@ -44,6 +44,8 @@ FIXME #4730 --- much more needed, don't have time to write this all up now
use core::prelude::*; use core::prelude::*;
use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl}; 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::borrowck::{err_out_of_scope};
use middle::mem_categorization::{cat_arg, cat_binding, cat_discr, cat_comp}; use middle::mem_categorization::{cat_arg, cat_binding, cat_discr, cat_comp};
use middle::mem_categorization::{cat_deref, cat_discr, cat_local, cat_self}; 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::{m_const, m_imm, m_mutbl};
use syntax::ast; use syntax::ast;
impl BorrowckCtxt { pub fn loan(bccx: @BorrowckCtxt,
fn loan(&self,
cmt: cmt, cmt: cmt,
scope_region: ty::Region, scope_region: ty::Region,
mutbl: ast::mutability) -> bckres<~[Loan]> { loan_kind: LoanKind) -> bckres<~[Loan]>
let mut lc = LoanContext { {
bccx: self, let mut lc = LoanContext {
scope_region: scope_region, bccx: bccx,
loans: ~[] scope_region: scope_region,
}; loans: ~[]
match lc.loan(cmt, mutbl, true) { };
Err(ref e) => return Err((*e)), match lc.loan(cmt, loan_kind, true) {
Ok(()) => {} Err(ref e) => return Err((*e)),
} Ok(()) => {}
// XXX: Workaround for borrow check bug.
Ok(copy lc.loans)
} }
// XXX: Workaround for borrow check bug.
Ok(copy lc.loans)
} }
struct LoanContext { struct LoanContext {
bccx: &BorrowckCtxt, bccx: @BorrowckCtxt,
// the region scope for which we must preserve the memory // the region scope for which we must preserve the memory
scope_region: ty::Region, scope_region: ty::Region,
@ -87,12 +88,13 @@ struct LoanContext {
} }
impl LoanContext { impl LoanContext {
fn tcx(&mut self) -> ty::ctxt { self.bccx.tcx } fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
fn loan(&mut self, fn loan(&mut self,
cmt: cmt, cmt: cmt,
req_mutbl: ast::mutability, loan_kind: LoanKind,
owns_lent_data: bool) -> bckres<()> { owns_lent_data: bool) -> bckres<()>
{
/*! /*!
* *
* The main routine. * The main routine.
@ -107,9 +109,9 @@ impl LoanContext {
* discussion in `issue_loan()`. * discussion in `issue_loan()`.
*/ */
debug!("loan(%s, %s)", debug!("loan(%s, %?)",
self.bccx.cmt_to_repr(cmt), self.bccx.cmt_to_repr(cmt),
self.bccx.mut_to_str(req_mutbl)); loan_kind);
let _i = indenter(); let _i = indenter();
// see stable() above; should only be called when `cmt` is lendable // see stable() above; should only be called when `cmt` is lendable
@ -127,15 +129,16 @@ impl LoanContext {
~"rvalue with a non-none lp"); ~"rvalue with a non-none lp");
} }
cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => { cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => {
let local_scope_id = self.tcx().region_map.get(&local_id); // FIXME(#4903)
self.issue_loan(cmt, ty::re_scope(local_scope_id), req_mutbl, 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) owns_lent_data)
} }
cat_stack_upvar(cmt) => { cat_stack_upvar(cmt) => {
self.loan(cmt, req_mutbl, owns_lent_data) self.loan(cmt, loan_kind, owns_lent_data)
} }
cat_discr(base, _) => { 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_field(_, m)) |
cat_comp(cmt_base, comp_index(_, m)) => { cat_comp(cmt_base, comp_index(_, m)) => {
@ -145,13 +148,13 @@ impl LoanContext {
// that case, it must also be embedded in an immutable // that case, it must also be embedded in an immutable
// location, or else the whole structure could be // location, or else the whole structure could be
// overwritten and the component along with it. // 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) owns_lent_data)
} }
cat_comp(cmt_base, comp_tuple) | cat_comp(cmt_base, comp_tuple) |
cat_comp(cmt_base, comp_anon_field) => { cat_comp(cmt_base, comp_anon_field) => {
// As above. // 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) owns_lent_data)
} }
cat_comp(cmt_base, comp_variant(enum_did)) => { cat_comp(cmt_base, comp_variant(enum_did)) => {
@ -159,10 +162,10 @@ impl LoanContext {
// variants, because if the enum value is overwritten then // variants, because if the enum value is overwritten then
// the memory changes type. // the memory changes type.
if ty::enum_is_univariant(self.bccx.tcx, enum_did) { 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) owns_lent_data)
} else { } else {
self.loan_unstable_deref(cmt, cmt_base, req_mutbl, self.loan_unstable_deref(cmt, cmt_base, loan_kind,
owns_lent_data) owns_lent_data)
} }
} }
@ -170,7 +173,7 @@ impl LoanContext {
// For unique pointers, the memory being pointed out is // For unique pointers, the memory being pointed out is
// unstable because if the unique pointer is overwritten // unstable because if the unique pointer is overwritten
// then the memory is freed. // 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) owns_lent_data)
} }
cat_deref(cmt_base, _, region_ptr(ast::m_mutbl, region)) => { 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, // loan out the base as well as the main memory. For example,
// if someone borrows `*b`, we want to borrow `b` as immutable // if someone borrows `*b`, we want to borrow `b` as immutable
// as well. // as well.
do self.loan(cmt_base, m_imm, false).chain |_| { do self.loan(cmt_base, TotalFreeze, false).chain |_| {
self.issue_loan(cmt, region, m_const, owns_lent_data) self.issue_loan(cmt, region, loan_kind, owns_lent_data)
} }
} }
cat_deref(_, _, unsafe_ptr) | cat_deref(_, _, unsafe_ptr) |
@ -199,66 +202,38 @@ impl LoanContext {
fn loan_stable_comp(&mut self, fn loan_stable_comp(&mut self,
cmt: cmt, cmt: cmt,
cmt_base: cmt, cmt_base: cmt,
req_mutbl: ast::mutability, loan_kind: LoanKind,
comp_mutbl: ast::mutability, comp_mutbl: ast::mutability,
owns_lent_data: bool) -> bckres<()> { owns_lent_data: bool) -> bckres<()>
// Determine the mutability that the base component must have, {
// given the required mutability of the pointer (`req_mutbl`) let base_kind = match (comp_mutbl, loan_kind) {
// and the declared mutability of the component (`comp_mutbl`). // Declared as "immutable" means: inherited mutability and
// This is surprisingly subtle. // hence mutable iff parent is mutable. So propagate
// // mutability on up.
// Note that the *declared* mutability of the component is not (m_imm, TotalFreeze) | (m_imm, PartialFreeze) => PartialFreeze,
// necessarily the same as cmt.mutbl, since a component (m_imm, TotalTake) | (m_imm, PartialTake) => PartialTake,
// declared as immutable but embedded in a mutable context
// becomes mutable. It's best to think of comp_mutbl as being // Declared as "mutable" means: always mutable no matter
// either MUTABLE or DEFAULT, not MUTABLE or IMMUTABLE. We // what the mutability of the base is. So that means we
// should really patch up the AST to reflect this distinction. // can weaken the condition on the base to PartialFreeze.
// // This implies that the user could freeze the base, but
// Let's consider the cases below: // that is ok since the even with an &T base, the mut
// // field will still be considered mutable.
// 1. mut required, mut declared: In this case, the base (_, TotalTake) | (_, PartialTake) |
// component must merely be const. The reason is that it (_, TotalFreeze) | (_, PartialFreeze) => {
// does not matter if the base component is borrowed as PartialFreeze
// 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`) // If we just need to guarantee the value won't be moved,
// // it doesn't matter what mutability the component was
// 2. mut required, imm declared: This would only be legal if // declared with.
// the component is embeded in a mutable context. However, (_, Immobile) => Immobile,
// 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)
}; };
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 // can use static for the scope because the base
// determines the lifetime, ultimately // 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) owns_lent_data)
} }
} }
@ -269,23 +244,23 @@ impl LoanContext {
fn loan_unstable_deref(&mut self, fn loan_unstable_deref(&mut self,
cmt: cmt, cmt: cmt,
cmt_base: cmt, cmt_base: cmt,
req_mutbl: ast::mutability, loan_kind: LoanKind,
owns_lent_data: bool) -> bckres<()> { owns_lent_data: bool) -> bckres<()> {
// Variant components: the base must be immutable, because // Variant components: the base must be immutable, because
// if it is overwritten, the types of the embedded data // if it is overwritten, the types of the embedded data
// could change. // 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() // 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) owns_lent_data)
} }
} }
fn issue_loan(&mut self, fn issue_loan(&mut self,
cmt: cmt, +cmt: cmt,
scope_ub: ty::Region, +scope_ub: ty::Region,
req_mutbl: ast::mutability, +loan_kind: LoanKind,
owns_lent_data: bool) -> bckres<()> { +owns_lent_data: bool) -> bckres<()> {
// Subtle: the `scope_ub` is the maximal lifetime of `cmt`. // Subtle: the `scope_ub` is the maximal lifetime of `cmt`.
// Therefore, if `cmt` owns the data being lent, then the // Therefore, if `cmt` owns the data being lent, then the
// scope of the loan must be less than `scope_ub`, or else the // scope of the loan must be less than `scope_ub`, or else the
@ -297,25 +272,15 @@ impl LoanContext {
// reborrowed. // reborrowed.
if !owns_lent_data || if !owns_lent_data ||
self.bccx.is_subregion_of(/*bad*/copy self.scope_region, self.bccx.is_subregion_of(self.scope_region, scope_ub)
scope_ub) { {
match req_mutbl { if loan_kind.is_take() && !cmt.mutbl.is_mutable() {
m_mutbl => { // We do not allow non-mutable data to be "taken"
// We do not allow non-mutable data to be loaned // under any circumstances.
// out as mutable under any circumstances. return Err(bckerr {
if cmt.mutbl != m_mutbl { cmt:cmt,
return Err(bckerr { code:err_mutbl(loan_kind)
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.loans.push(Loan { self.loans.push(Loan {
@ -323,8 +288,9 @@ impl LoanContext {
// loan process does not apply at all. // loan process does not apply at all.
lp: cmt.lp.get(), lp: cmt.lp.get(),
cmt: cmt, cmt: cmt,
mutbl: req_mutbl kind: loan_kind
}); });
return Ok(()); return Ok(());
} else { } else {
// The loan being requested lives longer than the data // The loan being requested lives longer than the data

View file

@ -368,7 +368,7 @@ pub enum bckerr_code {
err_mut_uniq, err_mut_uniq,
err_mut_variant, err_mut_variant,
err_root_not_permitted, 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_root_scope(ty::Region, ty::Region), // superscope, subscope
err_out_of_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` // shorthand for something that fails with `bckerr` or succeeds with `T`
pub type bckres<T> = Result<T, bckerr>; pub type bckres<T> = Result<T, bckerr>;
#[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 /// 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` /// maps computed by `gather_loans` that are then used by `check_loans`
/// ///
@ -420,6 +431,22 @@ pub fn save_and_restore_managed<T:Copy,U>(save_and_restore_t: @mut T,
move u 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 /// Creates and returns a new root_map
pub impl root_map_key : to_bytes::IterBytes { pub impl root_map_key : to_bytes::IterBytes {
@ -520,9 +547,9 @@ pub impl BorrowckCtxt {
fn bckerr_to_str(&self, err: bckerr) -> ~str { fn bckerr_to_str(&self, err: bckerr) -> ~str {
match err.code { match err.code {
err_mutbl(req) => { err_mutbl(lk) => {
fmt!("creating %s alias to %s", fmt!("creating %s alias to %s",
self.mut_to_str(req), self.loan_kind_to_str(lk),
self.cmt_to_str(err.cmt)) self.cmt_to_str(err.cmt))
} }
err_mut_uniq => { err_mut_uniq => {
@ -599,9 +626,17 @@ pub impl BorrowckCtxt {
mc.mut_to_str(mutbl) 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 { fn loan_to_repr(&self, loan: &Loan) -> ~str {
fmt!("Loan(lp=%?, cmt=%s, mutbl=%?)", fmt!("Loan(lp=%?, cmt=%s, kind=%?)",
loan.lp, self.cmt_to_repr(loan.cmt), loan.mutbl) loan.lp, self.cmt_to_repr(loan.cmt), loan.kind)
} }
} }

View file

@ -190,10 +190,10 @@ impl PreserveCtxt {
// otherwise we have no guarantee the pointer will stay // otherwise we have no guarantee the pointer will stay
// live, so we must root the pointer (i.e., inc the ref // live, so we must root the pointer (i.e., inc the ref
// count) for the duration of the loan. // 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() { if cmt.cat.derefs_through_mutable_box() {
self.attempt_root(cmt, base, derefs) self.attempt_root(cmt, base, derefs)
} else if base.mutbl == m_imm { } else if base.mutbl.is_immutable() {
let non_rooting_ctxt = PreserveCtxt { let non_rooting_ctxt = PreserveCtxt {
root_managed_data: false, root_managed_data: false,
..*self ..*self
@ -293,14 +293,11 @@ impl PreserveCtxt {
// the base is preserved, but if we are not mutable then // the base is preserved, but if we are not mutable then
// purity is required // purity is required
Ok(PcOk) => { Ok(PcOk) => {
match cmt_base.mutbl { if !cmt_base.mutbl.is_immutable() {
m_mutbl | m_const => { Ok(PcIfPure(bckerr {cmt:cmt, code:code}))
Ok(PcIfPure(bckerr {cmt:cmt, code:code})) } else {
Ok(PcOk)
} }
m_imm => {
Ok(PcOk)
}
}
} }
// the base requires purity too, that's fine // the base requires purity too, that's fine

View file

@ -108,6 +108,14 @@ pub enum special_kind {
sk_heap_upvar 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 // a complete categorization of a value indicating where it originated
// and how it is located, as well as the mutability of the memory in // and how it is located, as well as the mutability of the memory in
// which the value is stored. // which the value is stored.
@ -115,12 +123,12 @@ pub enum special_kind {
// note: cmt stands for "categorized mutable type". // note: cmt stands for "categorized mutable type".
#[deriving_eq] #[deriving_eq]
pub struct cmt_ { pub struct cmt_ {
id: ast::node_id, // id of expr/pat producing this value id: ast::node_id, // id of expr/pat producing this value
span: span, // span of same expr/pat span: span, // span of same expr/pat
cat: categorization, // categorization of expr cat: categorization, // categorization of expr
lp: Option<@loan_path>, // loan path for expr, if any lp: Option<@loan_path>, // loan path for expr, if any
mutbl: ast::mutability, // mutability of expr as lvalue mutbl: MutabilityCategory, // mutability of expr as lvalue
ty: ty::t // type of the expr ty: ty::t // type of the expr
} }
pub type cmt = @cmt_; pub type cmt = @cmt_;
@ -298,8 +306,55 @@ pub struct mem_categorization_ctxt {
method_map: typeck::method_map, method_map: typeck::method_map,
} }
pub impl &mem_categorization_ctxt { impl ToStr for MutabilityCategory {
fn cat_expr(expr: @ast::expr) -> cmt { 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) { match self.tcx.adjustments.find(&expr.id) {
None => { None => {
// No adjustments. // 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 { adjustment: &ty::AutoAdjustment) -> cmt {
let mut cmt = self.cat_expr_unadjusted(expr); let mut cmt = self.cat_expr_unadjusted(expr);
for uint::range(1, adjustment.autoderefs+1) |deref| { for uint::range(1, adjustment.autoderefs+1) |deref| {
@ -332,7 +388,7 @@ pub impl &mem_categorization_ctxt {
return cmt; 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", debug!("cat_expr: id=%d expr=%s",
expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr())); 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, span: span,
expr_ty: ty::t, expr_ty: ty::t,
def: ast::def) -> cmt { def: ast::def) -> cmt {
@ -409,7 +466,7 @@ pub impl &mem_categorization_ctxt {
span:span, span:span,
cat:cat_special(sk_static_item), cat:cat_special(sk_static_item),
lp:None, lp:None,
mutbl:m_imm, mutbl: McImmutable,
ty:expr_ty ty:expr_ty
} }
} }
@ -420,7 +477,7 @@ pub impl &mem_categorization_ctxt {
// m: mutability of the argument // m: mutability of the argument
// lp: loan path, must be none for aliasable things // 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) { let lp = match ty::resolved_mode(self.tcx, mode) {
ast::by_copy => Some(@lp_arg(vid)), ast::by_copy => Some(@lp_arg(vid)),
ast::by_ref => None, ast::by_ref => None,
@ -438,7 +495,7 @@ pub impl &mem_categorization_ctxt {
span:span, span:span,
cat:cat_arg(vid), cat:cat_arg(vid),
lp:lp, lp:lp,
mutbl:m, mutbl: m,
ty:expr_ty ty:expr_ty
} }
} }
@ -458,7 +515,7 @@ pub impl &mem_categorization_ctxt {
span:span, span:span,
cat:cat, cat:cat,
lp:loan_path, lp:loan_path,
mutbl:m_imm, mutbl: McImmutable,
ty:expr_ty ty:expr_ty
} }
} }
@ -485,7 +542,7 @@ pub impl &mem_categorization_ctxt {
span:span, span:span,
cat:cat_special(sk_heap_upvar), cat:cat_special(sk_heap_upvar),
lp:None, lp:None,
mutbl:m_imm, mutbl:McImmutable,
ty:expr_ty ty:expr_ty
} }
} }
@ -493,7 +550,7 @@ pub impl &mem_categorization_ctxt {
} }
ast::def_local(vid, mutbl) => { ast::def_local(vid, mutbl) => {
let m = if mutbl {m_mutbl} else {m_imm}; let m = if mutbl {McDeclared} else {McImmutable};
@cmt_ { @cmt_ {
id:id, id:id,
span:span, span:span,
@ -511,14 +568,15 @@ pub impl &mem_categorization_ctxt {
span:span, span:span,
cat:cat_local(vid), cat:cat_local(vid),
lp:Some(@lp_local(vid)), lp:Some(@lp_local(vid)),
mutbl:m_imm, mutbl:McImmutable,
ty:expr_ty ty:expr_ty
} }
} }
} }
} }
fn cat_variant<N: ast_node>(arg: N, fn cat_variant<N: ast_node>(&self,
arg: N,
enum_did: ast::def_id, enum_did: ast::def_id,
cmt: cmt) -> cmt { cmt: cmt) -> cmt {
@cmt_ { @cmt_ {
@ -526,18 +584,18 @@ pub impl &mem_categorization_ctxt {
span: arg.span(), span: arg.span(),
cat: cat_comp(cmt, comp_variant(enum_did)), cat: cat_comp(cmt, comp_variant(enum_did)),
lp: cmt.lp.map(|l| @lp_comp(*l, 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) ty: self.tcx.ty(arg)
} }
} }
fn cat_rvalue<N: ast_node>(elt: N, expr_ty: ty::t) -> cmt { fn cat_rvalue<N: ast_node>(&self, elt: N, expr_ty: ty::t) -> cmt {
@cmt_ { @cmt_ {
id:elt.id(), id:elt.id(),
span:elt.span(), span:elt.span(),
cat:cat_rvalue, cat:cat_rvalue,
lp:None, lp:None,
mutbl:m_imm, mutbl:McImmutable,
ty:expr_ty ty:expr_ty
} }
} }
@ -546,17 +604,21 @@ pub impl &mem_categorization_ctxt {
/// component is inherited from the base it is a part of. For /// component is inherited from the base it is a part of. For
/// example, a record field is mutable if it is declared mutable /// example, a record field is mutable if it is declared mutable
/// or if the container is mutable. /// or if the container is mutable.
fn inherited_mutability(base_m: ast::mutability, fn inherited_mutability(&self,
comp_m: ast::mutability) -> ast::mutability { base_m: MutabilityCategory,
comp_m: ast::mutability) -> MutabilityCategory
{
match comp_m { match comp_m {
m_imm => {base_m} // imm: as mutable as the container m_imm => base_m.inherit(),
m_mutbl | m_const => {comp_m} m_const => McReadOnly,
m_mutbl => McDeclared
} }
} }
/// The `field_id` parameter is the ID of the enclosing expression or /// 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. /// pattern. It is used to determine which variant of an enum is in use.
fn cat_field<N:ast_node>(node: N, fn cat_field<N:ast_node>(&self,
node: N,
base_cmt: cmt, base_cmt: cmt,
f_name: ast::ident, f_name: ast::ident,
field_id: ast::node_id) -> cmt { field_id: ast::node_id) -> cmt {
@ -584,7 +646,8 @@ pub impl &mem_categorization_ctxt {
} }
} }
fn cat_deref_fn<N:ast_node>(node: N, fn cat_deref_fn<N:ast_node>(&self,
node: N,
base_cmt: cmt, base_cmt: cmt,
deref_cnt: uint) -> 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 // know what type lies at the other end, so we just call it
// `()` (the empty tuple). // `()` (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); return self.cat_deref_common(node, base_cmt, deref_cnt, mt);
} }
fn cat_deref<N:ast_node>(node: N, fn cat_deref<N:ast_node>(&self,
node: N,
base_cmt: cmt, base_cmt: cmt,
deref_cnt: uint) -> 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); return self.cat_deref_common(node, base_cmt, deref_cnt, mt);
} }
fn cat_deref_common<N:ast_node>(node: N, fn cat_deref_common<N:ast_node>(&self,
node: N,
base_cmt: cmt, base_cmt: cmt,
deref_cnt: uint, deref_cnt: uint,
mt: ty::mt) -> cmt mt: ty::mt) -> cmt
@ -644,7 +710,7 @@ pub impl &mem_categorization_ctxt {
self.inherited_mutability(base_cmt.mutbl, mt.mutbl) self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
} }
gc_ptr(*) | region_ptr(_, _) | unsafe_ptr => { 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<N: ast_node>(elt: N, base_cmt: cmt) -> cmt { fn cat_index<N: ast_node>(&self,
elt: N,
base_cmt: cmt) -> cmt {
let mt = match ty::index(self.tcx, base_cmt.ty) { let mt = match ty::index(self.tcx, base_cmt.ty) {
Some(mt) => mt, Some(mt) => mt,
None => { None => {
@ -700,7 +768,7 @@ pub impl &mem_categorization_ctxt {
self.inherited_mutability(base_cmt.mutbl, mt.mutbl) self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
} }
gc_ptr(_) | region_ptr(_, _) | unsafe_ptr => { 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 ty:mt.ty
}; };
comp(elt, deref_cmt, base_cmt.ty, m, mt.ty) comp(elt, deref_cmt, base_cmt.ty, m, mt)
} }
deref_comp(_) => { deref_comp(_) => {
// fixed-length vectors have no deref // fixed-length vectors have no deref
let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl); 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<N: ast_node>(elt: N, of_cmt: cmt, fn comp<N: ast_node>(elt: N, of_cmt: cmt,
vect: ty::t, mutbl: ast::mutability, vect: ty::t, mutbl: MutabilityCategory,
ty: ty::t) -> cmt 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) ); let index_lp = of_cmt.lp.map(|lp| @lp_comp(*lp, comp) );
@cmt_ { @cmt_ {
id:elt.id(), id:elt.id(),
@ -736,46 +804,55 @@ pub impl &mem_categorization_ctxt {
cat:cat_comp(of_cmt, comp), cat:cat_comp(of_cmt, comp),
lp:index_lp, lp:index_lp,
mutbl:mutbl, mutbl:mutbl,
ty:ty ty:mt.ty
} }
} }
} }
fn cat_tuple_elt<N: ast_node>(elt: N, cmt: cmt) -> cmt { fn cat_tuple_elt<N: ast_node>(&self,
elt: N,
cmt: cmt) -> cmt {
@cmt_ { @cmt_ {
id: elt.id(), id: elt.id(),
span: elt.span(), span: elt.span(),
cat: cat_comp(cmt, comp_tuple), cat: cat_comp(cmt, comp_tuple),
lp: cmt.lp.map(|l| @lp_comp(*l, 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) ty: self.tcx.ty(elt)
} }
} }
fn cat_anon_struct_field<N: ast_node>(elt: N, cmt: cmt) -> cmt { fn cat_anon_struct_field<N: ast_node>(&self,
elt: N,
cmt: cmt) -> cmt {
@cmt_ { @cmt_ {
id: elt.id(), id: elt.id(),
span: elt.span(), span: elt.span(),
cat: cat_comp(cmt, comp_anon_field), cat: cat_comp(cmt, comp_anon_field),
lp: cmt.lp.map(|l| @lp_comp(*l, 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) 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_ { @cmt_ {
id:expr.id, id:expr.id,
span:expr.span, span:expr.span,
cat:cat_special(sk_method), cat:cat_special(sk_method),
lp:None, lp:None,
mutbl:m_imm, mutbl:McImmutable,
ty:expr_ty 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 // Here, `cmt` is the categorization for the value being
// matched and pat is the pattern it is being matched against. // 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 { match cat {
cat_special(sk_method) => ~"method", cat_special(sk_method) => ~"method",
cat_special(sk_static_item) => ~"static_item", 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 { match mutbl {
m_mutbl => ~"mutable", m_mutbl => ~"mutable",
m_const => ~"const", 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 { match ptr {
uniq_ptr => ~"~", uniq_ptr => ~"~",
gc_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 { match comp {
comp_field(fld, _) => self.tcx.sess.str_of(fld), comp_field(fld, _) => self.tcx.sess.str_of(fld),
comp_index(*) => ~"[]", 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 { match *lp {
lp_local(node_id) => { lp_local(node_id) => {
fmt!("local(%d)", node_id) fmt!("local(%d)", node_id)
@ -971,17 +1048,17 @@ pub impl &mem_categorization_ctxt {
} }
} }
fn cmt_to_repr(cmt: cmt) -> ~str { fn cmt_to_repr(&self, cmt: cmt) -> ~str {
fmt!("{%s id:%d m:%s lp:%s ty:%s}", fmt!("{%s id:%d m:%? lp:%s ty:%s}",
self.cat_to_repr(cmt.cat), self.cat_to_repr(cmt.cat),
cmt.id, cmt.id,
self.mut_to_str(cmt.mutbl), cmt.mutbl,
cmt.lp.map_default(~"none", |p| self.lp_to_str(*p) ), cmt.lp.map_default(~"none", |p| self.lp_to_str(*p) ),
ty_to_str(self.tcx, cmt.ty)) ty_to_str(self.tcx, cmt.ty))
} }
fn cmt_to_str(cmt: cmt) -> ~str { fn cmt_to_str(&self, cmt: cmt) -> ~str {
let mut_str = self.mut_to_str(cmt.mutbl); let mut_str = cmt.mutbl.to_user_str();
match cmt.cat { match cmt.cat {
cat_special(sk_method) => ~"method", cat_special(sk_method) => ~"method",
cat_special(sk_static_item) => ~"static item", 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) region_to_str(self.tcx, r)
} }
} }

View file

@ -116,26 +116,26 @@ pub impl Reflector {
fn bracketed(&mut self, fn bracketed(&mut self,
bracket_name: ~str, bracket_name: ~str,
+extra: ~[ValueRef], +extra: ~[ValueRef],
inner: &fn()) { inner: &fn(&mut Reflector)) {
// XXX: Bad copy. // XXX: Bad copy.
self.visit(~"enter_" + bracket_name, copy extra); self.visit(~"enter_" + bracket_name, copy extra);
inner(); inner(self);
self.visit(~"leave_" + bracket_name, extra); self.visit(~"leave_" + bracket_name, extra);
} }
fn vstore_name_and_extra(&mut self, fn vstore_name_and_extra(&mut self,
t: ty::t, t: ty::t,
vstore: ty::vstore, vstore: ty::vstore) -> (~str, ~[ValueRef])
f: fn(+s: ~str,+v: ~[ValueRef])) { {
match vstore { match vstore {
ty::vstore_fixed(n) => { ty::vstore_fixed(n) => {
let extra = vec::append(~[self.c_uint(n)], let extra = vec::append(~[self.c_uint(n)],
self.c_size_and_align(t)); self.c_size_and_align(t));
f(~"fixed", extra) (~"fixed", extra)
} }
ty::vstore_slice(_) => f(~"slice", ~[]), ty::vstore_slice(_) => (~"slice", ~[]),
ty::vstore_uniq => f(~"uniq", ~[]), ty::vstore_uniq => (~"uniq", ~[]),
ty::vstore_box => f(~"box", ~[]) 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_f32) => self.leaf(~"f32"),
ty::ty_float(ast::ty_f64) => self.leaf(~"f64"), 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) => { ty::ty_estr(vst) => {
do self.vstore_name_and_extra(t, vst) |name, extra| { let (name, extra) = self.vstore_name_and_extra(t, vst);
self.visit(~"estr_" + name, extra) self.visit(~"estr_" + name, extra)
}
} }
ty::ty_evec(mt, vst) => { ty::ty_evec(mt, vst) => {
do self.vstore_name_and_extra(t, vst) |name, extra| { let (name, extra) = self.vstore_name_and_extra(t, vst);
self.visit(~"evec_" + name, extra + let extra = extra + self.c_mt(mt);
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) => { ty::ty_rec(fields) => {
do self.bracketed(~"rec", let extra = ~[self.c_uint(vec::len(fields))]
~[self.c_uint(vec::len(fields))] + self.c_size_and_align(t);
+ self.c_size_and_align(t)) { do self.bracketed(~"rec", extra) |this| {
for fields.eachi |i, field| { for fields.eachi |i, field| {
self.visit(~"rec_field", let extra = ~[this.c_uint(i),
~[self.c_uint(i), this.c_slice(
self.c_slice( bcx.ccx().sess.str_of(field.ident))]
bcx.ccx().sess.str_of(field.ident))] + this.c_mt(field.mt);
+ self.c_mt(field.mt)); this.visit(~"rec_field", extra);
} }
} }
} }
ty::ty_tup(tys) => { ty::ty_tup(tys) => {
do self.bracketed(~"tup", let extra = ~[self.c_uint(vec::len(tys))]
~[self.c_uint(vec::len(tys))] + self.c_size_and_align(t);
+ self.c_size_and_align(t)) { do self.bracketed(~"tup", extra) |this| {
for tys.eachi |i, t| { for tys.eachi |i, t| {
self.visit(~"tup_field", let extra = ~[this.c_uint(i), this.c_tydesc(*t)];
~[self.c_uint(i), this.visit(~"tup_field", extra);
self.c_tydesc(*t)]); }
} }
}
} }
// FIXME (#2594): fetch constants out of intrinsic // FIXME (#2594): fetch constants out of intrinsic
@ -242,20 +255,21 @@ pub impl Reflector {
} }
ty::ty_struct(did, ref substs) => { ty::ty_struct(did, ref substs) => {
let bcx = self.bcx; let bcx = self.bcx;
let tcx = bcx.ccx().tcx; let tcx = bcx.ccx().tcx;
let fields = ty::struct_fields(tcx, did, substs); let fields = ty::struct_fields(tcx, did, substs);
do self.bracketed(~"class", ~[self.c_uint(fields.len())] let extra = ~[self.c_uint(fields.len())]
+ self.c_size_and_align(t)) { + self.c_size_and_align(t);
for fields.eachi |i, field| { do self.bracketed(~"class", extra) |this| {
self.visit(~"class_field", for fields.eachi |i, field| {
~[self.c_uint(i), let extra = ~[this.c_uint(i),
self.c_slice( this.c_slice(
bcx.ccx().sess.str_of(field.ident))] bcx.ccx().sess.str_of(field.ident))]
+ self.c_mt(field.mt)); + this.c_mt(field.mt);
} this.visit(~"class_field", extra);
} }
}
} }
// FIXME (#2595): visiting all the variants in turn is probably // FIXME (#2595): visiting all the variants in turn is probably
@ -267,20 +281,20 @@ pub impl Reflector {
let tcx = bcx.ccx().tcx; let tcx = bcx.ccx().tcx;
let variants = ty::substd_enum_variants(tcx, did, substs); let variants = ty::substd_enum_variants(tcx, did, substs);
do self.bracketed(~"enum", let extra = ~[self.c_uint(vec::len(variants))]
~[self.c_uint(vec::len(variants))] + self.c_size_and_align(t);
+ self.c_size_and_align(t)) { do self.bracketed(~"enum", extra) |this| {
for variants.eachi |i, v| { for variants.eachi |i, v| {
do self.bracketed(~"enum_variant", let extra1 = ~[this.c_uint(i),
~[self.c_uint(i), this.c_int(v.disr_val),
self.c_int(v.disr_val), this.c_uint(vec::len(v.args)),
self.c_uint(vec::len(v.args)), this.c_slice(
self.c_slice( bcx.ccx().sess.str_of(v.name))];
bcx.ccx().sess.str_of(v.name))]) { do this.bracketed(~"enum_variant", extra1) |this| {
for v.args.eachi |j, a| { for v.args.eachi |j, a| {
self.visit(~"enum_variant_field", let extra = ~[this.c_uint(j),
~[self.c_uint(j), this.c_tydesc(*a)];
self.c_tydesc(*a)]); this.visit(~"enum_variant_field", extra);
} }
} }
} }
@ -291,13 +305,17 @@ pub impl Reflector {
ty::ty_trait(_, _, _) => self.leaf(~"trait"), ty::ty_trait(_, _, _) => self.leaf(~"trait"),
ty::ty_infer(_) => self.leaf(~"infer"), ty::ty_infer(_) => self.leaf(~"infer"),
ty::ty_err => self.leaf(~"err"), 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_self => self.leaf(~"self"),
ty::ty_type => self.leaf(~"type"), ty::ty_type => self.leaf(~"type"),
ty::ty_opaque_box => self.leaf(~"opaque_box"), ty::ty_opaque_box => self.leaf(~"opaque_box"),
ty::ty_opaque_closure_ptr(ck) => { ty::ty_opaque_closure_ptr(ck) => {
let ckval = ast_sigil_constant(ck); let ckval = ast_sigil_constant(ck);
self.visit(~"closure_ptr", ~[self.c_uint(ckval)]) let extra = ~[self.c_uint(ckval)];
self.visit(~"closure_ptr", extra)
} }
} }
} }
@ -312,14 +330,14 @@ pub impl Reflector {
ast::by_copy => 5u ast::by_copy => 5u
} }
}; };
self.visit(~"fn_input", let extra = ~[self.c_uint(i),
~[self.c_uint(i),
self.c_uint(modeval), self.c_uint(modeval),
self.c_tydesc(arg.ty)]); self.c_tydesc(arg.ty)];
self.visit(~"fn_input", extra);
} }
self.visit(~"fn_output", let extra = ~[self.c_uint(retval),
~[self.c_uint(retval), self.c_tydesc(sig.output)];
self.c_tydesc(sig.output)]); self.visit(~"fn_output", extra);
} }
} }

View file

@ -1934,7 +1934,6 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
} }
cache.insert(ty_id, TC_NONE); cache.insert(ty_id, TC_NONE);
debug!("computing contents of %s", ty_to_str(cx, ty));
let _i = indenter(); let _i = indenter();
let mut result = match get(ty).sty { let mut result = match get(ty).sty {
@ -2085,8 +2084,6 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
result = result + TC_BIG; result = result + TC_BIG;
} }
debug!("result = %s", result.to_str());
cache.insert(ty_id, result); cache.insert(ty_id, result);
return result; return result;
} }

View file

@ -118,11 +118,10 @@ pub impl CombineFields {
// A remains a subtype of B. Actually, there are other options, // A remains a subtype of B. Actually, there are other options,
// but that's the route we choose to take. // but that's the route we choose to take.
self.infcx.unify(&node_a, &node_b, |new_root, new_rank| { let (new_root, new_rank) = self.infcx.unify(&node_a, &node_b);
self.set_var_to_merged_bounds(new_root, self.set_var_to_merged_bounds(new_root,
&a_bounds, &b_bounds, &a_bounds, &b_bounds,
new_rank) new_rank)
})
} }
/// make variable a subtype of T /// make variable a subtype of T

View file

@ -832,7 +832,7 @@ pub impl RegionVarBindings {
(re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => { (re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => {
self.combine_vars( self.combine_vars(
self.lubs, a, b, span, 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(*))) => { (re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => {
self.combine_vars( self.combine_vars(
self.glbs, a, b, span, 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, a: Region,
b: Region, b: Region,
span: span, span: span,
relate: &fn(old_r: Region, new_r: Region) -> cres<()>) relate: &fn(self: &mut RegionVarBindings,
old_r: Region,
new_r: Region) -> cres<()>)
-> cres<Region> { -> cres<Region> {
let vars = TwoRegions { a: a, b: b }; let vars = TwoRegions { a: a, b: b };
match combines.find(&vars) { match combines.find(&vars) {
@ -926,8 +928,8 @@ pub impl RegionVarBindings {
if self.in_snapshot() { if self.in_snapshot() {
self.undo_log.push(AddCombination(combines, vars)); self.undo_log.push(AddCombination(combines, vars));
} }
do relate(a, re_infer(ReVar(c))).then { do relate(self, a, re_infer(ReVar(c))).then {
do relate(b, re_infer(ReVar(c))).then { do relate(self, b, re_infer(ReVar(c))).then {
debug!("combine_vars() c=%?", c); debug!("combine_vars() c=%?", c);
Ok(re_infer(ReVar(c))) Ok(re_infer(ReVar(c)))
} }
@ -1035,7 +1037,8 @@ pub impl RegionVarBindings {
*/ */
fn resolve_regions(&mut self) { fn resolve_regions(&mut self) {
debug!("RegionVarBindings: resolve_regions()"); 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(); let mut graph = self.construct_graph();
self.expansion(&mut graph); self.expansion(&mut graph);
self.contraction(&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 { fn construct_graph(&mut self) -> Graph {
@ -1257,14 +1260,14 @@ impl RegionVarBindings {
for uint::range(0, num_edges) |edge_idx| { for uint::range(0, num_edges) |edge_idx| {
match graph.edges[edge_idx].constraint { 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, a_id, Outgoing, edge_idx);
insert_edge(&mut graph, b_id, Incoming, 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); 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); insert_edge(&mut graph, a_id, Outgoing, edge_idx);
} }
} }
@ -1285,17 +1288,17 @@ impl RegionVarBindings {
} }
fn expansion(&mut self, graph: &mut Graph) { 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 { match edge.constraint {
ConstrainRegSubVar(copy a_region, copy b_vid) => { ConstrainRegSubVar(a_region, b_vid) => {
let b_node = &mut graph.nodes[*b_vid]; let b_node = &mut nodes[*b_vid];
self.expand_node(a_region, b_vid, b_node) self.expand_node(a_region, b_vid, b_node)
} }
ConstrainVarSubVar(copy a_vid, copy b_vid) => { ConstrainVarSubVar(a_vid, b_vid) => {
match graph.nodes[*a_vid].value { match nodes[*a_vid].value {
NoValue | ErrorValue => false, NoValue | ErrorValue => false,
Value(copy a_region) => { Value(a_region) => {
let b_node = &mut graph.nodes[*b_vid]; let b_node = &mut nodes[*b_vid];
self.expand_node(a_region, b_vid, b_node) self.expand_node(a_region, b_vid, b_node)
} }
} }
@ -1325,7 +1328,7 @@ impl RegionVarBindings {
return true; return true;
} }
Value(copy cur_region) => { Value(cur_region) => {
let lub = self.lub_concrete_regions(a_region, cur_region); let lub = self.lub_concrete_regions(a_region, cur_region);
if lub == cur_region { if lub == cur_region {
return false; return false;
@ -1345,23 +1348,23 @@ impl RegionVarBindings {
} }
fn contraction(&mut self, graph: &mut Graph) { 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 { match edge.constraint {
ConstrainRegSubVar(*) => { ConstrainRegSubVar(*) => {
// This is an expansion constraint. Ignore. // This is an expansion constraint. Ignore.
false false
} }
ConstrainVarSubVar(copy a_vid, copy b_vid) => { ConstrainVarSubVar(a_vid, b_vid) => {
match graph.nodes[*b_vid].value { match nodes[*b_vid].value {
NoValue | ErrorValue => false, NoValue | ErrorValue => false,
Value(copy b_region) => { Value(b_region) => {
let a_node = &mut graph.nodes[*a_vid]; let a_node = &mut nodes[*a_vid];
self.contract_node(a_vid, a_node, b_region) self.contract_node(a_vid, a_node, b_region)
} }
} }
} }
ConstrainVarSubReg(copy a_vid, copy b_region) => { ConstrainVarSubReg(a_vid, b_region) => {
let a_node = &mut graph.nodes[*a_vid]; let a_node = &mut nodes[*a_vid];
self.contract_node(a_vid, a_node, b_region) self.contract_node(a_vid, a_node, b_region)
} }
} }
@ -1387,7 +1390,7 @@ impl RegionVarBindings {
false // no change false // no change
} }
Value(copy a_region) => { Value(a_region) => {
match a_node.classification { match a_node.classification {
Expanding => { Expanding => {
check_node(self, a_vid, a_node, a_region, b_region) check_node(self, a_vid, a_node, a_region, b_region)
@ -1438,29 +1441,10 @@ impl RegionVarBindings {
} }
} }
fn iterate_until_fixed_point(&mut self, fn extract_values_and_report_conflicts(
tag: ~str, &mut self,
graph: &mut Graph, graph: &Graph) -> ~[GraphNodeValue]
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] {
let dup_map = TwoRegionsMap(); let dup_map = TwoRegionsMap();
graph.nodes.mapi(|idx, node| { graph.nodes.mapi(|idx, node| {
match node.value { match node.value {
@ -1525,7 +1509,7 @@ impl RegionVarBindings {
} }
fn report_error_for_expanding_node(&mut self, fn report_error_for_expanding_node(&mut self,
graph: &mut Graph, graph: &Graph,
dup_map: TwoRegionsMap, dup_map: TwoRegionsMap,
node_idx: RegionVid) { node_idx: RegionVid) {
// Errors in expanding nodes result from a lower-bound that is // 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, fn report_error_for_contracting_node(&mut self,
graph: &mut Graph, graph: &Graph,
dup_map: TwoRegionsMap, dup_map: TwoRegionsMap,
node_idx: RegionVid) { node_idx: RegionVid) {
// Errors in contracting nodes result from two upper-bounds // Errors in contracting nodes result from two upper-bounds
@ -1632,7 +1616,7 @@ impl RegionVarBindings {
} }
fn collect_concrete_regions(&mut self, fn collect_concrete_regions(&mut self,
graph: &mut Graph, graph: &Graph,
orig_node_idx: RegionVid, orig_node_idx: RegionVid,
dir: Direction) dir: Direction)
-> ~[SpannedRegion] { -> ~[SpannedRegion] {
@ -1676,7 +1660,7 @@ impl RegionVarBindings {
} }
fn each_edge(&mut self, fn each_edge(&mut self,
graph: &mut Graph, graph: &Graph,
node_idx: RegionVid, node_idx: RegionVid,
dir: Direction, dir: Direction,
op: fn(edge: &GraphEdge) -> bool) { 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);
}

View file

@ -43,9 +43,10 @@ pub trait UnifyVid<T> {
} }
pub impl InferCtxt { pub impl InferCtxt {
fn get<T:Copy, V:Copy Eq Vid UnifyVid<T>>(&mut self, fn get<T:Copy, V:Copy+Eq+Vid+UnifyVid<T>>(
+vid: V) &mut self,
-> Node<V, T> { +vid: V) -> Node<V, T>
{
/*! /*!
* *
* Find the root node for `vid`. This uses the standard * 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 * http://en.wikipedia.org/wiki/Disjoint-set_data_structure
*/ */
let tcx = self.tcx;
let vb = UnifyVid::appropriate_vals_and_bindings(self); let vb = UnifyVid::appropriate_vals_and_bindings(self);
let vid_u = vid.to_uint(); return helper(tcx, vb, vid);
match vb.vals.find(vid_u) {
None => { fn helper<T:Copy, V:Copy+Eq+Vid>(
self.tcx.sess.bug(fmt!("failed lookup of vid `%u`", vid_u)); tcx: ty::ctxt,
} vb: &mut ValsAndBindings<V,T>,
Some(ref var_val) => { vid: V) -> Node<V, T>
match (*var_val) { {
Redirect(vid) => { let vid_u = vid.to_uint();
let node: Node<V,T> = self.get(vid); match vb.vals.find(vid_u) {
if node.root != vid { None => {
// Path compression tcx.sess.bug(fmt!(
vb.vals.insert(vid.to_uint(), Redirect(node.root)); "failed lookup of vid `%u`", vid_u));
}
Some(ref var_val) => {
match *var_val {
Redirect(vid) => {
let node: Node<V,T> = 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! * Sets the value for `vid` to `new_v`. `vid` MUST be a root node!
*/ */
let vb = UnifyVid::appropriate_vals_and_bindings(self); debug!("Updating variable %s to %s",
let old_v = vb.vals.get(vid.to_uint()); vid.to_str(), new_v.inf_str(self));
vb.bindings.push((vid, old_v));
vb.vals.insert(vid.to_uint(), new_v);
debug!("Updating variable %s from %s to %s", { // FIXME(#4903)---borrow checker is not flow sensitive
vid.to_str(), old_v.inf_str(self), new_v.inf_str(self)); 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<T:Copy InferStr, V:Copy Vid ToStr UnifyVid<T>, R>( fn unify<T:Copy InferStr, V:Copy Vid ToStr UnifyVid<T>>(
&mut self, &mut self,
node_a: &Node<V, T>, node_a: &Node<V, T>,
node_b: &Node<V, T>, node_b: &Node<V, T>) -> (V, uint)
op: &fn(new_root: V, new_rank: uint) -> R {
) -> R {
// Rank optimization: if you don't know what it is, check // Rank optimization: if you don't know what it is, check
// out <http://en.wikipedia.org/wiki/Disjoint-set_data_structure> // out <http://en.wikipedia.org/wiki/Disjoint-set_data_structure>
@ -113,17 +126,17 @@ pub impl InferCtxt {
// a has greater rank, so a should become b's parent, // a has greater rank, so a should become b's parent,
// i.e., b should redirect to a. // i.e., b should redirect to a.
self.set(node_b.root, Redirect(node_a.root)); 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 { } else if node_a.rank < node_b.rank {
// b has greater rank, so a should redirect to b. // b has greater rank, so a should redirect to b.
self.set(node_a.root, Redirect(node_b.root)); self.set(node_a.root, Redirect(node_b.root));
op(node_b.root, node_b.rank) (node_b.root, node_b.rank)
} else { } else {
// If equal, redirect one to the other and increment the // If equal, redirect one to the other and increment the
// other's rank. // other's rank.
assert node_a.rank == node_b.rank; assert node_a.rank == node_b.rank;
self.set(node_b.root, Redirect(node_a.root)); 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| { let (new_root, new_rank) = self.unify(&node_a, &node_b);
self.set(new_root, Root(combined, new_rank)); self.set(new_root, Root(combined, new_rank));
});
return uok(); return uok();
} }

View file

@ -184,25 +184,30 @@ impl<T: Owned> &MutexARC<T> {
*/ */
#[inline(always)] #[inline(always)]
unsafe fn access<U>(blk: fn(x: &mut T) -> U) -> U { unsafe fn access<U>(blk: fn(x: &mut T) -> U) -> U {
let state = unsafe { get_shared_mutable_state(&self.x) }; unsafe {
// Borrowck would complain about this if the function were not already let state = get_shared_mutable_state(&self.x);
// unsafe. See borrow_rwlock, far below. // Borrowck would complain about this if the function were
do (&state.lock).lock { // not already unsafe. See borrow_rwlock, far below.
check_poison(true, state.failed); do (&(*state).lock).lock {
let _z = PoisonOnFail(&mut state.failed); check_poison(true, (*state).failed);
blk(&mut state.data) let _z = PoisonOnFail(&mut (*state).failed);
blk(&mut (*state).data)
}
} }
} }
/// As access(), but with a condvar, as sync::mutex.lock_cond(). /// As access(), but with a condvar, as sync::mutex.lock_cond().
#[inline(always)] #[inline(always)]
unsafe fn access_cond<U>(blk: fn(x: &x/mut T, c: &c/Condvar) -> U) -> U { unsafe fn access_cond<U>(blk: fn(x: &x/mut T, c: &c/Condvar) -> U) -> U {
let state = unsafe { get_shared_mutable_state(&self.x) }; unsafe {
do (&state.lock).lock_cond |cond| { let state = get_shared_mutable_state(&self.x);
check_poison(true, state.failed); do (&(*state).lock).lock_cond |cond| {
let _z = PoisonOnFail(&mut state.failed); check_poison(true, (*state).failed);
blk(&mut state.data, let _z = PoisonOnFail(&mut (*state).failed);
&Condvar { is_mutex: true, failed: &mut state.failed, blk(&mut (*state).data,
cond: cond }) &Condvar {is_mutex: true,
failed: &mut (*state).failed,
cond: cond })
}
} }
} }
} }
@ -285,8 +290,10 @@ pub fn RWARC<T: Const Owned>(user_data: T) -> RWARC<T> {
* Create a reader/writer ARC with the supplied data and a specified number * Create a reader/writer ARC with the supplied data and a specified number
* of condvars (as sync::rwlock_with_condvars). * of condvars (as sync::rwlock_with_condvars).
*/ */
pub fn rw_arc_with_condvars<T: Const Owned>(user_data: T, pub fn rw_arc_with_condvars<T: Const Owned>(
num_condvars: uint) -> RWARC<T> { user_data: T,
num_condvars: uint) -> RWARC<T>
{
let data = let data =
RWARCInner { lock: rwlock_with_condvars(num_condvars), RWARCInner { lock: rwlock_with_condvars(num_condvars),
failed: false, data: move user_data }; failed: false, data: move user_data };
@ -315,23 +322,28 @@ impl<T: Const Owned> &RWARC<T> {
*/ */
#[inline(always)] #[inline(always)]
fn write<U>(blk: fn(x: &mut T) -> U) -> U { fn write<U>(blk: fn(x: &mut T) -> U) -> U {
let state = unsafe { get_shared_mutable_state(&self.x) }; unsafe {
do borrow_rwlock(state).write { let state = get_shared_mutable_state(&self.x);
check_poison(false, state.failed); do (*borrow_rwlock(state)).write {
let _z = PoisonOnFail(&mut state.failed); check_poison(false, (*state).failed);
blk(&mut state.data) let _z = PoisonOnFail(&mut (*state).failed);
blk(&mut (*state).data)
}
} }
} }
/// As write(), but with a condvar, as sync::rwlock.write_cond(). /// As write(), but with a condvar, as sync::rwlock.write_cond().
#[inline(always)] #[inline(always)]
fn write_cond<U>(blk: fn(x: &x/mut T, c: &c/Condvar) -> U) -> U { fn write_cond<U>(blk: fn(x: &x/mut T, c: &c/Condvar) -> U) -> U {
let state = unsafe { get_shared_mutable_state(&self.x) }; unsafe {
do borrow_rwlock(state).write_cond |cond| { let state = get_shared_mutable_state(&self.x);
check_poison(false, state.failed); do (*borrow_rwlock(state)).write_cond |cond| {
let _z = PoisonOnFail(&mut state.failed); check_poison(false, (*state).failed);
blk(&mut state.data, let _z = PoisonOnFail(&mut (*state).failed);
&Condvar { is_mutex: false, failed: &mut state.failed, blk(&mut (*state).data,
cond: cond }) &Condvar {is_mutex: false,
failed: &mut (*state).failed,
cond: cond})
}
} }
} }
/** /**
@ -369,11 +381,14 @@ impl<T: Const Owned> &RWARC<T> {
* ~~~ * ~~~
*/ */
fn write_downgrade<U>(blk: fn(v: RWWriteMode<T>) -> U) -> U { fn write_downgrade<U>(blk: fn(v: RWWriteMode<T>) -> U) -> U {
let state = unsafe { get_shared_mutable_state(&self.x) }; unsafe {
do borrow_rwlock(state).write_downgrade |write_mode| { let state = get_shared_mutable_state(&self.x);
check_poison(false, state.failed); do (*borrow_rwlock(state)).write_downgrade |write_mode| {
blk(RWWriteMode((&mut state.data, move write_mode, check_poison(false, (*state).failed);
PoisonOnFail(&mut state.failed)))) blk(RWWriteMode((&mut (*state).data,
move write_mode,
PoisonOnFail(&mut (*state).failed))))
}
} }
} }
@ -417,8 +432,8 @@ pub fn unwrap_rw_arc<T: Const Owned>(arc: RWARC<T>) -> T {
// lock it. This wraps the unsafety, with the justification that the 'lock' // lock it. This wraps the unsafety, with the justification that the 'lock'
// field is never overwritten; only 'failed' and 'data'. // field is never overwritten; only 'failed' and 'data'.
#[doc(hidden)] #[doc(hidden)]
fn borrow_rwlock<T: Const Owned>(state: &r/mut RWARCInner<T>) -> &r/RWlock { fn borrow_rwlock<T: Const Owned>(state: *const RWARCInner<T>) -> *RWlock {
unsafe { cast::transmute(&mut state.lock) } unsafe { cast::transmute(&const (*state).lock) }
} }
// FIXME (#3154) ice with struct/&<T> prevents these from being structs. // FIXME (#3154) ice with struct/&<T> prevents these from being structs.

View file

@ -81,7 +81,8 @@ impl <T: Ord> PriorityQueue<T> {
/// Push an item onto the queue /// Push an item onto the queue
fn push(&mut self, item: T) { fn push(&mut self, item: T) {
self.data.push(item); 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 /// Optimized version of a push followed by a pop
@ -179,7 +180,8 @@ impl <T: Ord> PriorityQueue<T> {
} }
priv fn siftdown(&mut self, pos: uint) { priv fn siftdown(&mut self, pos: uint) {
self.siftdown_range(pos, self.len()); let len = self.len();
self.siftdown_range(pos, len);
} }
} }

View file

@ -134,10 +134,11 @@ pub impl<V> SmallIntMap<V> {
pub impl<V: Copy> SmallIntMap<V> { pub impl<V: Copy> SmallIntMap<V> {
fn update_with_key(&mut self, key: uint, val: V, fn update_with_key(&mut self, key: uint, val: V,
ff: fn(uint, V, V) -> V) -> bool { ff: fn(uint, V, V) -> V) -> bool {
match self.find(&key) { let new_val = match self.find(&key) {
None => self.insert(key, val), None => val,
Some(orig) => self.insert(key, ff(key, copy *orig, 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 { fn update(&mut self, key: uint, newval: V, ff: fn(V, V) -> V) -> bool {

View file

@ -673,45 +673,45 @@ fn remove<K: Ord, V>(node: &mut Option<~TreeNode<K, V>>, key: &K) -> bool {
} }
}; };
if this { if !this {
*node = None; let left_level = save.left.map_default(0, |x| x.level);
return true; let right_level = save.right.map_default(0, |x| x.level);
}
let left_level = save.left.map_default(0, |x| x.level); // re-balance, if necessary
let right_level = save.right.map_default(0, |x| x.level); if left_level < save.level - 1 || right_level < save.level - 1 {
save.level -= 1;
// re-balance, if necessary if right_level > save.level {
if left_level < save.level - 1 || right_level < save.level - 1 { do save.right.mutate |mut x| { x.level = save.level; x }
save.level -= 1; }
if right_level > save.level { skew(save);
do save.right.mutate |mut x| { x.level = save.level; x }
} match save.right {
Some(ref mut right) => {
skew(save); skew(right);
match right.right {
match save.right { Some(ref mut x) => { skew(x) },
Some(ref mut right) => { None => ()
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); return removed;
match save.right {
Some(ref mut x) => { split(x) },
None => ()
}
} }
removed
} }
} }
*node = None;
return true;
} }
#[cfg(test)] #[cfg(test)]

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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));
}

View file

@ -8,20 +8,27 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use core::hashmap::linear::LinearSet;
struct Foo { struct Foo {
x: uint n: LinearSet<int>,
} }
struct Bar { impl Foo {
foo: 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() { fn main() {
let mut b = Bar { foo: Foo { x: 3 } }; let mut f = Foo { n: LinearSet::new() };
let p = &b; //~ NOTE prior loan as immutable granted here bar(&mut f);
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));
} }

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {}