librustc: Implement write guards for borrowing @mut
to &
or &mut
. r=nmatsakis
This commit is contained in:
parent
8bde2c1d65
commit
f405e41d7a
38 changed files with 522 additions and 365 deletions
|
@ -216,7 +216,7 @@ pub mod raw {
|
|||
}
|
||||
|
||||
pub unsafe fn push_slow<T>(v: &mut @[const T], initval: T) {
|
||||
reserve_at_least(v, v.len() + 1u);
|
||||
reserve_at_least(&mut *v, v.len() + 1u);
|
||||
push_fast(v, move initval);
|
||||
}
|
||||
|
||||
|
@ -234,7 +234,7 @@ pub mod raw {
|
|||
pub unsafe fn reserve<T>(v: &mut @[const T], n: uint) {
|
||||
// Only make the (slow) call into the runtime if we have to
|
||||
if capacity(*v) < n {
|
||||
let ptr: **VecRepr = transmute(copy v);
|
||||
let ptr: **VecRepr = transmute(v);
|
||||
rustrt::vec_reserve_shared_actual(sys::get_type_desc::<T>(),
|
||||
ptr, n as libc::size_t);
|
||||
}
|
||||
|
|
|
@ -95,8 +95,8 @@ pub mod at_vec;
|
|||
pub mod str;
|
||||
|
||||
pub mod ptr;
|
||||
pub mod managed;
|
||||
pub mod owned;
|
||||
pub mod managed;
|
||||
|
||||
|
||||
/* Core language traits */
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
#[forbid(deprecated_mode)];
|
||||
#[forbid(deprecated_pattern)];
|
||||
|
||||
use cast::transmute;
|
||||
use cmp::{Eq, Ord};
|
||||
use managed::raw::BoxRepr;
|
||||
use prelude::*;
|
||||
use ptr;
|
||||
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
#[forbid(deprecated_pattern)];
|
||||
//! Runtime calls emitted by the compiler.
|
||||
|
||||
use cast::transmute;
|
||||
use libc::{c_char, c_void, size_t, uintptr_t};
|
||||
use managed::raw::BoxRepr;
|
||||
use str;
|
||||
use sys;
|
||||
|
||||
|
@ -24,6 +26,11 @@ use gc::{cleanup_stack_for_failure, gc, Word};
|
|||
#[allow(non_camel_case_types)]
|
||||
pub type rust_task = c_void;
|
||||
|
||||
#[cfg(target_word_size = "32")]
|
||||
const FROZEN_BIT: uint = 0x80000000;
|
||||
#[cfg(target_word_size = "64")]
|
||||
const FROZEN_BIT: uint = 0x8000000000000000;
|
||||
|
||||
extern mod rustrt {
|
||||
#[rust_stack]
|
||||
unsafe fn rust_upcall_exchange_malloc(td: *c_char, size: uintptr_t)
|
||||
|
@ -56,6 +63,15 @@ pub unsafe fn rt_fail_bounds_check(file: *c_char, line: size_t,
|
|||
}
|
||||
}
|
||||
|
||||
pub unsafe fn rt_fail_borrowed() {
|
||||
let msg = "borrowed";
|
||||
do str::as_buf(msg) |msg_p, _| {
|
||||
do str::as_buf("???") |file_p, _| {
|
||||
rt_fail_(msg_p as *c_char, file_p as *c_char, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rt(exchange_malloc)]
|
||||
#[lang="exchange_malloc"]
|
||||
pub unsafe fn rt_exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
|
||||
|
@ -86,6 +102,29 @@ pub unsafe fn rt_free(ptr: *c_char) {
|
|||
rustrt::rust_upcall_free(ptr);
|
||||
}
|
||||
|
||||
#[lang="borrow_as_imm"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn borrow_as_imm(a: *u8) {
|
||||
let a: *mut BoxRepr = transmute(a);
|
||||
(*a).header.ref_count |= FROZEN_BIT;
|
||||
}
|
||||
|
||||
#[lang="return_to_mut"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn return_to_mut(a: *u8) {
|
||||
let a: *mut BoxRepr = transmute(a);
|
||||
(*a).header.ref_count &= !FROZEN_BIT;
|
||||
}
|
||||
|
||||
#[lang="check_not_borrowed"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn check_not_borrowed(a: *u8) {
|
||||
let a: *mut BoxRepr = transmute(a);
|
||||
if ((*a).header.ref_count & FROZEN_BIT) != 0 {
|
||||
rt_fail_borrowed();
|
||||
}
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
// mode: rust;
|
||||
// fill-column: 78;
|
||||
|
|
|
@ -80,7 +80,7 @@ pub fn push_char(s: &mut ~str, ch: char) {
|
|||
else { 6u };
|
||||
let len = len(*s);
|
||||
let new_len = len + nb;
|
||||
reserve_at_least(s, new_len);
|
||||
reserve_at_least(&mut *s, new_len);
|
||||
let off = len;
|
||||
do as_buf(*s) |buf, _len| {
|
||||
let buf: *mut u8 = ::cast::reinterpret_cast(&buf);
|
||||
|
@ -164,7 +164,7 @@ pub fn push_str_no_overallocate(lhs: &mut ~str, rhs: &str) {
|
|||
unsafe {
|
||||
let llen = lhs.len();
|
||||
let rlen = rhs.len();
|
||||
reserve(lhs, llen + rlen);
|
||||
reserve(&mut *lhs, llen + rlen);
|
||||
do as_buf(*lhs) |lbuf, _llen| {
|
||||
do as_buf(rhs) |rbuf, _rlen| {
|
||||
let dst = ptr::offset(lbuf, llen);
|
||||
|
@ -181,7 +181,7 @@ pub fn push_str(lhs: &mut ~str, rhs: &str) {
|
|||
unsafe {
|
||||
let llen = lhs.len();
|
||||
let rlen = rhs.len();
|
||||
reserve_at_least(lhs, llen + rlen);
|
||||
reserve_at_least(&mut *lhs, llen + rlen);
|
||||
do as_buf(*lhs) |lbuf, _llen| {
|
||||
do as_buf(rhs) |rbuf, _rlen| {
|
||||
let dst = ptr::offset(lbuf, llen);
|
||||
|
@ -2056,18 +2056,18 @@ 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(s, s.len() + 1);
|
||||
reserve_at_least(&mut *s, s.len() + 1);
|
||||
do as_buf(*s) |buf, len| {
|
||||
let buf: *mut u8 = ::cast::reinterpret_cast(&buf);
|
||||
*ptr::mut_offset(buf, len) = b;
|
||||
}
|
||||
set_len(s, s.len() + 1);
|
||||
set_len(&mut *s, s.len() + 1);
|
||||
}
|
||||
|
||||
/// Appends a vector of bytes to a string. (Not UTF-8 safe).
|
||||
unsafe fn push_bytes(s: &mut ~str, bytes: &[u8]) {
|
||||
reserve_at_least(s, s.len() + bytes.len());
|
||||
for vec::each(bytes) |byte| { push_byte(s, *byte); }
|
||||
reserve_at_least(&mut *s, s.len() + bytes.len());
|
||||
for vec::each(bytes) |byte| { push_byte(&mut *s, *byte); }
|
||||
}
|
||||
|
||||
/// Removes the last byte from a string and returns it. (Not UTF-8 safe).
|
||||
|
@ -2090,7 +2090,7 @@ pub mod raw {
|
|||
|
||||
/// Sets the length of the string and adds the null terminator
|
||||
pub unsafe fn set_len(v: &mut ~str, new_len: uint) {
|
||||
let v: **vec::raw::VecRepr = cast::transmute(copy v);
|
||||
let v: **vec::raw::VecRepr = cast::transmute(v);
|
||||
let repr: *vec::raw::VecRepr = *v;
|
||||
(*repr).unboxed.fill = new_len + 1u;
|
||||
let null = ptr::mut_offset(ptr::mut_addr_of(&((*repr).unboxed.data)),
|
||||
|
|
|
@ -187,7 +187,7 @@ fn each_ancestor(list: &mut AncestorList,
|
|||
forward_blk: fn(TaskGroupInner) -> bool,
|
||||
last_generation: uint) -> bool {
|
||||
// Need to swap the list out to use it, to appease borrowck.
|
||||
let tmp_list = util::replace(list, AncestorList(None));
|
||||
let tmp_list = util::replace(&mut *list, AncestorList(None));
|
||||
let (coalesce_this, early_break) =
|
||||
iterate(&tmp_list, bail_opt, forward_blk, last_generation);
|
||||
// What should our next ancestor end up being?
|
||||
|
@ -289,7 +289,7 @@ fn each_ancestor(list: &mut AncestorList,
|
|||
fn with_parent_tg<U>(parent_group: &mut Option<TaskGroupArc>,
|
||||
blk: fn(TaskGroupInner) -> U) -> U {
|
||||
// If this trips, more likely the problem is 'blk' failed inside.
|
||||
let tmp_arc = option::swap_unwrap(parent_group);
|
||||
let tmp_arc = option::swap_unwrap(&mut *parent_group);
|
||||
let result = do access_group(&tmp_arc) |tg_opt| { blk(tg_opt) };
|
||||
*parent_group = move Some(move tmp_arc);
|
||||
move result
|
||||
|
@ -363,7 +363,7 @@ fn AutoNotify(chan: Chan<TaskResult>) -> AutoNotify {
|
|||
|
||||
fn enlist_in_taskgroup(state: TaskGroupInner, me: *rust_task,
|
||||
is_member: bool) -> bool {
|
||||
let newstate = util::replace(state, None);
|
||||
let newstate = util::replace(&mut *state, None);
|
||||
// If 'None', the group was failing. Can't enlist.
|
||||
if newstate.is_some() {
|
||||
let group = option::unwrap(move newstate);
|
||||
|
@ -379,7 +379,7 @@ fn enlist_in_taskgroup(state: TaskGroupInner, me: *rust_task,
|
|||
// NB: Runs in destructor/post-exit context. Can't 'fail'.
|
||||
fn leave_taskgroup(state: TaskGroupInner, me: *rust_task,
|
||||
is_member: bool) {
|
||||
let newstate = util::replace(state, None);
|
||||
let newstate = util::replace(&mut *state, None);
|
||||
// If 'None', already failing and we've already gotten a kill signal.
|
||||
if newstate.is_some() {
|
||||
let group = option::unwrap(move newstate);
|
||||
|
|
|
@ -472,7 +472,7 @@ pub fn shift<T>(v: &mut ~[T]) -> T unsafe {
|
|||
assert capacity(v) >= ln;
|
||||
// Pretend like we have the original length so we can use
|
||||
// the vector copy_memory to overwrite the hole we just made
|
||||
raw::set_len(v, ln);
|
||||
raw::set_len(&mut *v, ln);
|
||||
|
||||
// Memcopy the head element (the one we want) to the location we just
|
||||
// popped. For the moment it unsafely exists at both the head and last
|
||||
|
@ -487,7 +487,7 @@ pub fn shift<T>(v: &mut ~[T]) -> T unsafe {
|
|||
raw::copy_memory(init_slice, tail_slice, next_ln);
|
||||
|
||||
// Set the new length. Now the vector is back to normal
|
||||
raw::set_len(v, next_ln);
|
||||
raw::set_len(&mut *v, next_ln);
|
||||
|
||||
// Swap out the element we want from the end
|
||||
let vp = raw::to_mut_ptr(*v);
|
||||
|
@ -592,7 +592,7 @@ pub fn swap_remove<T>(v: &mut ~[T], index: uint) -> T {
|
|||
#[inline(always)]
|
||||
pub fn push<T>(v: &mut ~[T], initval: T) {
|
||||
unsafe {
|
||||
let repr: **raw::VecRepr = ::cast::transmute(copy v);
|
||||
let repr: **raw::VecRepr = ::cast::transmute(&mut *v);
|
||||
let fill = (**repr).unboxed.fill;
|
||||
if (**repr).unboxed.alloc > fill {
|
||||
push_fast(v, initval);
|
||||
|
@ -616,30 +616,30 @@ unsafe fn push_fast<T>(v: &mut ~[T], initval: T) {
|
|||
|
||||
#[inline(never)]
|
||||
fn push_slow<T>(v: &mut ~[T], initval: T) {
|
||||
reserve_at_least(v, v.len() + 1u);
|
||||
reserve_at_least(&mut *v, v.len() + 1u);
|
||||
unsafe { push_fast(v, initval) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn push_all<T: Copy>(v: &mut ~[T], rhs: &[const T]) {
|
||||
reserve(v, v.len() + rhs.len());
|
||||
reserve(&mut *v, v.len() + rhs.len());
|
||||
|
||||
for uint::range(0u, rhs.len()) |i| {
|
||||
push(v, unsafe { raw::get(rhs, i) })
|
||||
push(&mut *v, unsafe { raw::get(rhs, i) })
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn push_all_move<T>(v: &mut ~[T], rhs: ~[T]) {
|
||||
let mut rhs = rhs; // FIXME(#3488)
|
||||
reserve(v, v.len() + rhs.len());
|
||||
reserve(&mut *v, v.len() + rhs.len());
|
||||
unsafe {
|
||||
do as_mut_buf(rhs) |p, len| {
|
||||
for uint::range(0, len) |i| {
|
||||
// FIXME #4204 Should be rusti::uninit() - don't need to zero
|
||||
let mut x = rusti::init();
|
||||
x <-> *ptr::mut_offset(p, i);
|
||||
push(v, x);
|
||||
push(&mut *v, x);
|
||||
}
|
||||
}
|
||||
raw::set_len(&mut rhs, 0);
|
||||
|
@ -657,7 +657,7 @@ pub fn truncate<T>(v: &mut ~[T], newlen: uint) {
|
|||
let mut dropped = rusti::init();
|
||||
dropped <-> *ptr::mut_offset(p, i);
|
||||
}
|
||||
raw::set_len(v, newlen);
|
||||
raw::set_len(&mut *v, newlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -731,7 +731,7 @@ pub pure fn append_mut<T: Copy>(lhs: ~[mut T], rhs: &[const T]) -> ~[mut T] {
|
|||
* * initval - The value for the new elements
|
||||
*/
|
||||
pub fn grow<T: Copy>(v: &mut ~[T], n: uint, initval: &T) {
|
||||
reserve_at_least(v, v.len() + n);
|
||||
reserve_at_least(&mut *v, v.len() + n);
|
||||
let mut i: uint = 0u;
|
||||
|
||||
while i < n {
|
||||
|
@ -754,7 +754,7 @@ pub fn grow<T: Copy>(v: &mut ~[T], n: uint, initval: &T) {
|
|||
* value
|
||||
*/
|
||||
pub fn grow_fn<T>(v: &mut ~[T], n: uint, op: iter::InitOp<T>) {
|
||||
reserve_at_least(v, v.len() + n);
|
||||
reserve_at_least(&mut *v, v.len() + n);
|
||||
let mut i: uint = 0u;
|
||||
while i < n {
|
||||
v.push(op(i));
|
||||
|
@ -772,7 +772,7 @@ pub fn grow_fn<T>(v: &mut ~[T], n: uint, op: iter::InitOp<T>) {
|
|||
*/
|
||||
pub fn grow_set<T: Copy>(v: &mut ~[T], index: uint, initval: &T, val: T) {
|
||||
let l = v.len();
|
||||
if index >= l { grow(v, index - l + 1u, initval); }
|
||||
if index >= l { grow(&mut *v, index - l + 1u, initval); }
|
||||
v[index] = val;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ fn find_rust_files(files: &mut ~[Path], path: &Path) {
|
|||
&& !contains(path.to_str(), ~"compile-fail")
|
||||
&& !contains(path.to_str(), ~"build") {
|
||||
for os::list_dir_path(path).each |p| {
|
||||
find_rust_files(files, *p);
|
||||
find_rust_files(&mut *files, *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,7 +291,7 @@ fn compile_upto(sess: Session, cfg: ast::crate_cfg,
|
|||
time(time_passes, ~"liveness checking", ||
|
||||
middle::liveness::check_crate(ty_cx, method_map, crate));
|
||||
|
||||
let (root_map, mutbl_map) =
|
||||
let (root_map, mutbl_map, write_guard_map) =
|
||||
time(time_passes, ~"borrow checking", ||
|
||||
middle::borrowck::check_crate(ty_cx, method_map,
|
||||
last_use_map, crate));
|
||||
|
@ -308,7 +308,8 @@ fn compile_upto(sess: Session, cfg: ast::crate_cfg,
|
|||
root_map: root_map,
|
||||
last_use_map: last_use_map,
|
||||
method_map: method_map,
|
||||
vtable_map: vtable_map};
|
||||
vtable_map: vtable_map,
|
||||
write_guard_map: write_guard_map};
|
||||
|
||||
time(time_passes, ~"translation", ||
|
||||
trans::base::trans_crate(sess, crate, ty_cx,
|
||||
|
|
|
@ -60,6 +60,7 @@ type maps = {
|
|||
last_use_map: middle::liveness::last_use_map,
|
||||
method_map: middle::typeck::method_map,
|
||||
vtable_map: middle::typeck::vtable_map,
|
||||
write_guard_map: middle::borrowck::write_guard_map,
|
||||
};
|
||||
|
||||
type decode_ctxt = @{
|
||||
|
|
|
@ -21,8 +21,8 @@ use core::prelude::*;
|
|||
|
||||
use middle::borrowck::{Loan, bckerr, borrowck_ctxt, cmt, inherent_mutability};
|
||||
use middle::borrowck::{req_maps, save_and_restore};
|
||||
use middle::mem_categorization::{cat_arg, cat_binding, cat_deref, cat_local};
|
||||
use middle::mem_categorization::{cat_rvalue, cat_special};
|
||||
use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref};
|
||||
use middle::mem_categorization::{cat_local, cat_rvalue, cat_special, gc_ptr};
|
||||
use middle::mem_categorization::{loan_path, lp_arg, lp_comp, lp_deref};
|
||||
use middle::mem_categorization::{lp_local};
|
||||
use middle::ty::{CopyValue, MoveValue, ReadValue};
|
||||
|
@ -54,6 +54,7 @@ enum check_loan_ctxt = @{
|
|||
};
|
||||
|
||||
// if we are enforcing purity, why are we doing so?
|
||||
#[deriving_eq]
|
||||
enum purity_cause {
|
||||
// enforcing purity because fn was declared pure:
|
||||
pc_pure_fn,
|
||||
|
@ -64,26 +65,6 @@ enum purity_cause {
|
|||
pc_cmt(bckerr)
|
||||
}
|
||||
|
||||
impl purity_cause : cmp::Eq {
|
||||
pure fn eq(&self, other: &purity_cause) -> bool {
|
||||
match (*self) {
|
||||
pc_pure_fn => {
|
||||
match (*other) {
|
||||
pc_pure_fn => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
pc_cmt(ref e0a) => {
|
||||
match (*other) {
|
||||
pc_cmt(ref e0b) => (*e0a) == (*e0b),
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pure fn ne(&self, other: &purity_cause) -> bool { !(*self).eq(other) }
|
||||
}
|
||||
|
||||
fn check_loans(bccx: borrowck_ctxt,
|
||||
req_maps: req_maps,
|
||||
crate: @ast::crate) {
|
||||
|
@ -100,18 +81,12 @@ fn check_loans(bccx: borrowck_ctxt,
|
|||
visit::visit_crate(*crate, clcx, vt);
|
||||
}
|
||||
|
||||
#[deriving_eq]
|
||||
enum assignment_type {
|
||||
at_straight_up,
|
||||
at_swap
|
||||
}
|
||||
|
||||
impl assignment_type : cmp::Eq {
|
||||
pure fn eq(&self, other: &assignment_type) -> bool {
|
||||
((*self) as uint) == ((*other) as uint)
|
||||
}
|
||||
pure fn ne(&self, other: &assignment_type) -> bool { !(*self).eq(other) }
|
||||
}
|
||||
|
||||
impl assignment_type {
|
||||
fn checked_by_liveness() -> bool {
|
||||
// the liveness pass guarantees that immutable local variables
|
||||
|
@ -410,6 +385,29 @@ impl check_loan_ctxt {
|
|||
}
|
||||
|
||||
self.bccx.add_to_mutbl_map(cmt);
|
||||
|
||||
// Check for and insert write guards as necessary.
|
||||
self.add_write_guards_if_necessary(cmt);
|
||||
}
|
||||
|
||||
fn add_write_guards_if_necessary(cmt: cmt) {
|
||||
match cmt.cat {
|
||||
cat_deref(base, deref_count, ptr_kind) => {
|
||||
self.add_write_guards_if_necessary(base);
|
||||
|
||||
match ptr_kind {
|
||||
gc_ptr(ast::m_mutbl) => {
|
||||
let key = { id: base.id, derefs: deref_count };
|
||||
self.bccx.write_guard_map.insert(key, ());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
cat_comp(base, _) => {
|
||||
self.add_write_guards_if_necessary(base);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_loan_conflicting_with_assignment(
|
||||
|
|
|
@ -454,10 +454,16 @@ impl gather_loan_ctxt {
|
|||
let e = {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() {
|
||||
Ok(pc_ok)
|
||||
} else {
|
||||
// you can treat mutable things as imm if you are pure
|
||||
debug!("imm required, must be pure");
|
||||
|
||||
Ok(pc_if_pure(e))
|
||||
}
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ 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};
|
||||
use middle::mem_categorization::{cat_special, cat_stack_upvar, comp_field};
|
||||
use middle::mem_categorization::{comp_index, comp_variant, region_ptr};
|
||||
use middle::mem_categorization::{comp_index, comp_variant, gc_ptr};
|
||||
use middle::mem_categorization::{region_ptr};
|
||||
use middle::ty;
|
||||
use util::common::indenter;
|
||||
|
||||
|
@ -162,7 +163,7 @@ impl LoanContext {
|
|||
self.loan_unstable_deref(cmt, cmt_base, req_mutbl)
|
||||
}
|
||||
cat_deref(_, _, unsafe_ptr) |
|
||||
cat_deref(_, _, gc_ptr) |
|
||||
cat_deref(_, _, gc_ptr(_)) |
|
||||
cat_deref(_, _, region_ptr(_)) => {
|
||||
// Aliased data is simply not lendable.
|
||||
self.bccx.tcx.sess.span_bug(
|
||||
|
|
|
@ -263,13 +263,15 @@ pub mod preserve;
|
|||
fn check_crate(tcx: ty::ctxt,
|
||||
method_map: typeck::method_map,
|
||||
last_use_map: liveness::last_use_map,
|
||||
crate: @ast::crate) -> (root_map, mutbl_map) {
|
||||
crate: @ast::crate)
|
||||
-> (root_map, mutbl_map, write_guard_map) {
|
||||
|
||||
let bccx = borrowck_ctxt_(@{tcx: tcx,
|
||||
method_map: method_map,
|
||||
last_use_map: last_use_map,
|
||||
root_map: root_map(),
|
||||
mutbl_map: HashMap(),
|
||||
write_guard_map: HashMap(),
|
||||
mut loaned_paths_same: 0,
|
||||
mut loaned_paths_imm: 0,
|
||||
mut stable_paths: 0,
|
||||
|
@ -293,7 +295,7 @@ fn check_crate(tcx: ty::ctxt,
|
|||
make_stat(bccx, bccx.req_pure_paths)));
|
||||
}
|
||||
|
||||
return (bccx.root_map, bccx.mutbl_map);
|
||||
return (bccx.root_map, bccx.mutbl_map, bccx.write_guard_map);
|
||||
|
||||
fn make_stat(bccx: borrowck_ctxt, stat: uint) -> ~str {
|
||||
let stat_f = stat as float;
|
||||
|
@ -310,6 +312,7 @@ type borrowck_ctxt_ = {tcx: ty::ctxt,
|
|||
last_use_map: liveness::last_use_map,
|
||||
root_map: root_map,
|
||||
mutbl_map: mutbl_map,
|
||||
write_guard_map: write_guard_map,
|
||||
|
||||
// Statistics:
|
||||
mut loaned_paths_same: uint,
|
||||
|
@ -322,10 +325,17 @@ enum borrowck_ctxt {
|
|||
borrowck_ctxt_(@borrowck_ctxt_)
|
||||
}
|
||||
|
||||
struct RootInfo {
|
||||
scope: ast::node_id,
|
||||
// This will be true if we need to freeze this box at runtime. This will
|
||||
// result in a call to `borrow_as_imm()` and `return_to_mut()`.
|
||||
freezes: bool // True if we need to freeze this box at runtime.
|
||||
}
|
||||
|
||||
// a map mapping id's of expressions of gc'd type (@T, @[], etc) where
|
||||
// the box needs to be kept live to the id of the scope for which they
|
||||
// must stay live.
|
||||
type root_map = HashMap<root_map_key, ast::node_id>;
|
||||
type root_map = HashMap<root_map_key, RootInfo>;
|
||||
|
||||
// the keys to the root map combine the `id` of the expression with
|
||||
// the number of types that it is autodereferenced. So, for example,
|
||||
|
@ -338,6 +348,10 @@ type root_map_key = {id: ast::node_id, derefs: uint};
|
|||
// this is used in trans for optimization purposes.
|
||||
type mutbl_map = HashMap<ast::node_id, ()>;
|
||||
|
||||
// A set containing IDs of expressions of gc'd type that need to have a write
|
||||
// guard.
|
||||
type write_guard_map = HashMap<root_map_key, ()>;
|
||||
|
||||
// Errors that can occur"]
|
||||
enum bckerr_code {
|
||||
err_mut_uniq,
|
||||
|
@ -447,14 +461,6 @@ impl root_map_key : to_bytes::IterBytes {
|
|||
|
||||
fn root_map() -> root_map {
|
||||
return HashMap();
|
||||
|
||||
pure fn root_map_key_eq(k1: &root_map_key, k2: &root_map_key) -> bool {
|
||||
k1.id == k2.id && k1.derefs == k2.derefs
|
||||
}
|
||||
|
||||
pure fn root_map_key_hash(k: &root_map_key) -> uint {
|
||||
(k.id << 4) as uint | k.derefs
|
||||
}
|
||||
}
|
||||
|
||||
// ___________________________________________________________________________
|
||||
|
|
|
@ -15,13 +15,14 @@
|
|||
|
||||
use core::prelude::*;
|
||||
|
||||
use middle::borrowck::{bckerr, bckerr_code, bckres, borrowck_ctxt, cmt};
|
||||
use middle::borrowck::{err_mut_uniq, err_mut_variant, err_out_of_root_scope};
|
||||
use middle::borrowck::{err_out_of_scope, err_root_not_permitted};
|
||||
use middle::borrowck::{RootInfo, bckerr, bckerr_code, bckres, borrowck_ctxt};
|
||||
use middle::borrowck::{cmt, err_mut_uniq, err_mut_variant};
|
||||
use middle::borrowck::{err_out_of_root_scope, err_out_of_scope};
|
||||
use middle::borrowck::{err_root_not_permitted};
|
||||
use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref};
|
||||
use middle::mem_categorization::{cat_discr, cat_local, cat_special};
|
||||
use middle::mem_categorization::{cat_stack_upvar, comp_field, comp_index};
|
||||
use middle::mem_categorization::{comp_variant, region_ptr};
|
||||
use middle::mem_categorization::{comp_variant, gc_ptr, region_ptr};
|
||||
use middle::ty;
|
||||
use util::common::indenter;
|
||||
|
||||
|
@ -180,14 +181,16 @@ priv impl &preserve_ctxt {
|
|||
// Unsafe pointers are the user's problem
|
||||
Ok(pc_ok)
|
||||
}
|
||||
cat_deref(base, derefs, gc_ptr) => {
|
||||
cat_deref(base, derefs, gc_ptr(*)) => {
|
||||
// GC'd pointers of type @MT: if this pointer lives in
|
||||
// immutable, stable memory, then everything is fine. But
|
||||
// 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));
|
||||
if base.mutbl == m_imm {
|
||||
if cmt.cat.derefs_through_mutable_box() {
|
||||
self.attempt_root(cmt, base, derefs)
|
||||
} else if base.mutbl == m_imm {
|
||||
let non_rooting_ctxt =
|
||||
preserve_ctxt({root_managed_data: false,.. **self});
|
||||
match (&non_rooting_ctxt).preserve(base) {
|
||||
|
@ -326,8 +329,10 @@ priv impl &preserve_ctxt {
|
|||
/// value live for longer than the current fn or else potentially
|
||||
/// require that an statically unbounded number of values be
|
||||
/// rooted (if a loop exists).
|
||||
fn attempt_root(cmt: cmt, base: cmt,
|
||||
derefs: uint) -> bckres<preserve_condition> {
|
||||
fn attempt_root(cmt: cmt,
|
||||
base: cmt,
|
||||
derefs: uint)
|
||||
-> bckres<preserve_condition> {
|
||||
if !self.root_managed_data {
|
||||
// normally, there is a root_ub; the only time that this
|
||||
// is none is when a boxed value is stored in an immutable
|
||||
|
@ -352,7 +357,12 @@ priv impl &preserve_ctxt {
|
|||
if self.bccx.is_subregion_of(self.scope_region, root_region) {
|
||||
debug!("Elected to root");
|
||||
let rk = {id: base.id, derefs: derefs};
|
||||
self.bccx.root_map.insert(rk, scope_id);
|
||||
// We freeze if and only if this is a *mutable* @ box that
|
||||
// we're borrowing into a pointer.
|
||||
self.bccx.root_map.insert(rk, RootInfo {
|
||||
scope: scope_id,
|
||||
freezes: cmt.cat.derefs_through_mutable_box()
|
||||
});
|
||||
return Ok(pc_ok);
|
||||
} else {
|
||||
debug!("Unable to root");
|
||||
|
|
|
@ -71,16 +71,19 @@ pub enum LangItem {
|
|||
ExchangeFreeFnLangItem, // 27
|
||||
MallocFnLangItem, // 28
|
||||
FreeFnLangItem, // 29
|
||||
BorrowAsImmFnLangItem, // 30
|
||||
ReturnToMutFnLangItem, // 31
|
||||
CheckNotBorrowedFnLangItem, // 32
|
||||
}
|
||||
|
||||
struct LanguageItems {
|
||||
items: [ Option<def_id> * 30 ]
|
||||
items: [ Option<def_id> * 33 ]
|
||||
}
|
||||
|
||||
impl LanguageItems {
|
||||
static pub fn new() -> LanguageItems {
|
||||
LanguageItems {
|
||||
items: [ None, ..30 ]
|
||||
items: [ None, ..33 ]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,6 +130,9 @@ impl LanguageItems {
|
|||
27 => "exchange_free",
|
||||
28 => "malloc",
|
||||
29 => "free",
|
||||
30 => "borrow_as_imm",
|
||||
31 => "return_to_mut",
|
||||
32 => "check_not_borrowed",
|
||||
|
||||
_ => "???"
|
||||
}
|
||||
|
@ -228,6 +234,15 @@ impl LanguageItems {
|
|||
pub fn free_fn(&const self) -> def_id {
|
||||
self.items[FreeFnLangItem as uint].get()
|
||||
}
|
||||
pub fn borrow_as_imm_fn(&const self) -> def_id {
|
||||
self.items[BorrowAsImmFnLangItem as uint].get()
|
||||
}
|
||||
pub fn return_to_mut_fn(&const self) -> def_id {
|
||||
self.items[ReturnToMutFnLangItem as uint].get()
|
||||
}
|
||||
pub fn check_not_borrowed_fn(&const self) -> def_id {
|
||||
self.items[CheckNotBorrowedFnLangItem as uint].get()
|
||||
}
|
||||
}
|
||||
|
||||
fn LanguageItemCollector(crate: @crate,
|
||||
|
@ -270,6 +285,10 @@ fn LanguageItemCollector(crate: @crate,
|
|||
item_refs.insert(~"exchange_free", ExchangeFreeFnLangItem as uint);
|
||||
item_refs.insert(~"malloc", MallocFnLangItem as uint);
|
||||
item_refs.insert(~"free", FreeFnLangItem as uint);
|
||||
item_refs.insert(~"borrow_as_imm", BorrowAsImmFnLangItem as uint);
|
||||
item_refs.insert(~"return_to_mut", ReturnToMutFnLangItem as uint);
|
||||
item_refs.insert(~"check_not_borrowed",
|
||||
CheckNotBorrowedFnLangItem as uint);
|
||||
|
||||
LanguageItemCollector {
|
||||
crate: crate,
|
||||
|
|
|
@ -62,6 +62,7 @@ use syntax::ast;
|
|||
use syntax::codemap::span;
|
||||
use syntax::print::pprust;
|
||||
|
||||
#[deriving_eq]
|
||||
enum categorization {
|
||||
cat_rvalue, // result of eval'ing some misc expr
|
||||
cat_special(special_kind), //
|
||||
|
@ -74,111 +75,18 @@ enum categorization {
|
|||
cat_discr(cmt, ast::node_id), // match discriminant (see preserve())
|
||||
}
|
||||
|
||||
impl categorization : cmp::Eq {
|
||||
pure fn eq(&self, other: &categorization) -> bool {
|
||||
match (*self) {
|
||||
cat_rvalue => {
|
||||
match (*other) {
|
||||
cat_rvalue => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
cat_special(e0a) => {
|
||||
match (*other) {
|
||||
cat_special(e0b) => e0a == e0b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
cat_local(e0a) => {
|
||||
match (*other) {
|
||||
cat_local(e0b) => e0a == e0b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
cat_binding(e0a) => {
|
||||
match (*other) {
|
||||
cat_binding(e0b) => e0a == e0b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
cat_arg(e0a) => {
|
||||
match (*other) {
|
||||
cat_arg(e0b) => e0a == e0b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
cat_stack_upvar(e0a) => {
|
||||
match (*other) {
|
||||
cat_stack_upvar(e0b) => e0a == e0b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
cat_deref(e0a, e1a, e2a) => {
|
||||
match (*other) {
|
||||
cat_deref(e0b, e1b, e2b) =>
|
||||
e0a == e0b && e1a == e1b && e2a == e2b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
cat_comp(e0a, e1a) => {
|
||||
match (*other) {
|
||||
cat_comp(e0b, e1b) => e0a == e0b && e1a == e1b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
cat_discr(e0a, e1a) => {
|
||||
match (*other) {
|
||||
cat_discr(e0b, e1b) => e0a == e0b && e1a == e1b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pure fn ne(&self, other: &categorization) -> bool { !(*self).eq(other) }
|
||||
}
|
||||
|
||||
// different kinds of pointers:
|
||||
#[deriving_eq]
|
||||
pub enum ptr_kind {
|
||||
uniq_ptr,
|
||||
gc_ptr,
|
||||
gc_ptr(ast::mutability),
|
||||
region_ptr(ty::Region),
|
||||
unsafe_ptr
|
||||
}
|
||||
|
||||
impl ptr_kind : cmp::Eq {
|
||||
pure fn eq(&self, other: &ptr_kind) -> bool {
|
||||
match (*self) {
|
||||
uniq_ptr => {
|
||||
match (*other) {
|
||||
uniq_ptr => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
gc_ptr => {
|
||||
match (*other) {
|
||||
gc_ptr => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
region_ptr(e0a) => {
|
||||
match (*other) {
|
||||
region_ptr(e0b) => e0a == e0b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
unsafe_ptr => {
|
||||
match (*other) {
|
||||
unsafe_ptr => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pure fn ne(&self, other: &ptr_kind) -> bool { !(*self).eq(other) }
|
||||
}
|
||||
|
||||
// I am coining the term "components" to mean "pieces of a data
|
||||
// structure accessible without a dereference":
|
||||
#[deriving_eq]
|
||||
pub enum comp_kind {
|
||||
comp_tuple, // elt in a tuple
|
||||
comp_anon_field, // anonymous field (in e.g.
|
||||
|
@ -190,45 +98,8 @@ pub enum comp_kind {
|
|||
ast::mutability) // mutability of vec content
|
||||
}
|
||||
|
||||
impl comp_kind : cmp::Eq {
|
||||
pure fn eq(&self, other: &comp_kind) -> bool {
|
||||
match (*self) {
|
||||
comp_tuple => {
|
||||
match (*other) {
|
||||
comp_tuple => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
comp_anon_field => {
|
||||
match (*other) {
|
||||
comp_anon_field => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
comp_variant(e0a) => {
|
||||
match (*other) {
|
||||
comp_variant(e0b) => e0a == e0b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
comp_field(e0a, e1a) => {
|
||||
match (*other) {
|
||||
comp_field(e0b, e1b) => e0a == e0b && e1a == e1b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
comp_index(e0a, e1a) => {
|
||||
match (*other) {
|
||||
comp_index(e0b, e1b) => e0a == e0b && e1a == e1b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pure fn ne(&self, other: &comp_kind) -> bool { !(*self).eq(other) }
|
||||
}
|
||||
|
||||
// different kinds of expressions we might evaluate
|
||||
#[deriving_eq]
|
||||
enum special_kind {
|
||||
sk_method,
|
||||
sk_static_item,
|
||||
|
@ -237,13 +108,6 @@ enum special_kind {
|
|||
sk_heap_upvar
|
||||
}
|
||||
|
||||
impl special_kind : cmp::Eq {
|
||||
pure fn eq(&self, other: &special_kind) -> bool {
|
||||
((*self) as uint) == ((*other) as uint)
|
||||
}
|
||||
pure fn ne(&self, other: &special_kind) -> bool { !(*self).eq(other) }
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -339,14 +203,17 @@ fn opt_deref_kind(t: ty::t) -> Option<deref_kind> {
|
|||
Some(deref_ptr(region_ptr((*f).meta.region)))
|
||||
}
|
||||
|
||||
ty::ty_box(*) |
|
||||
ty::ty_evec(_, ty::vstore_box) |
|
||||
ty::ty_box(mt) |
|
||||
ty::ty_evec(mt, ty::vstore_box) => {
|
||||
Some(deref_ptr(gc_ptr(mt.mutbl)))
|
||||
}
|
||||
|
||||
ty::ty_estr(ty::vstore_box) => {
|
||||
Some(deref_ptr(gc_ptr))
|
||||
Some(deref_ptr(gc_ptr(ast::m_imm)))
|
||||
}
|
||||
|
||||
ty::ty_fn(ref f) if (*f).meta.proto == ast::ProtoBox => {
|
||||
Some(deref_ptr(gc_ptr))
|
||||
Some(deref_ptr(gc_ptr(ast::m_imm)))
|
||||
}
|
||||
|
||||
ty::ty_ptr(*) => {
|
||||
|
@ -764,7 +631,7 @@ impl &mem_categorization_ctxt {
|
|||
// not loanable.
|
||||
match ptr {
|
||||
uniq_ptr => {Some(@lp_deref(*l, ptr))}
|
||||
gc_ptr | region_ptr(_) | unsafe_ptr => {None}
|
||||
gc_ptr(*) | region_ptr(_) | unsafe_ptr => {None}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -774,7 +641,7 @@ impl &mem_categorization_ctxt {
|
|||
uniq_ptr => {
|
||||
self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
|
||||
}
|
||||
gc_ptr | region_ptr(_) | unsafe_ptr => {
|
||||
gc_ptr(*) | region_ptr(_) | unsafe_ptr => {
|
||||
mt.mutbl
|
||||
}
|
||||
};
|
||||
|
@ -820,7 +687,7 @@ impl &mem_categorization_ctxt {
|
|||
uniq_ptr => {
|
||||
self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
|
||||
}
|
||||
gc_ptr | region_ptr(_) | unsafe_ptr => {
|
||||
gc_ptr(_) | region_ptr(_) | unsafe_ptr => {
|
||||
mt.mutbl
|
||||
}
|
||||
};
|
||||
|
@ -1027,7 +894,7 @@ impl &mem_categorization_ctxt {
|
|||
fn ptr_sigil(ptr: ptr_kind) -> ~str {
|
||||
match ptr {
|
||||
uniq_ptr => ~"~",
|
||||
gc_ptr => ~"@",
|
||||
gc_ptr(_) => ~"@",
|
||||
region_ptr(_) => ~"&",
|
||||
unsafe_ptr => ~"*"
|
||||
}
|
||||
|
@ -1160,3 +1027,34 @@ fn field_mutbl(tcx: ty::ctxt,
|
|||
|
||||
return None;
|
||||
}
|
||||
|
||||
impl categorization {
|
||||
fn derefs_through_mutable_box(&const self) -> bool {
|
||||
match *self {
|
||||
cat_deref(_, _, gc_ptr(ast::m_mutbl)) => {
|
||||
true
|
||||
}
|
||||
cat_deref(subcmt, _, _) |
|
||||
cat_comp(subcmt, _) |
|
||||
cat_discr(subcmt, _) |
|
||||
cat_stack_upvar(subcmt) => {
|
||||
subcmt.cat.derefs_through_mutable_box()
|
||||
}
|
||||
cat_rvalue |
|
||||
cat_special(*) |
|
||||
cat_local(*) |
|
||||
cat_binding(*) |
|
||||
cat_arg(*) => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_mutable_box(&const self) -> bool {
|
||||
match *self {
|
||||
cat_deref(_, _, gc_ptr(ast::m_mutbl)) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3466,12 +3466,12 @@ impl Resolver {
|
|||
for module_.children.each_ref |ident, namebindings| {
|
||||
debug!("(computing exports) maybe export '%s'",
|
||||
self.session.str_of(*ident));
|
||||
self.add_exports_of_namebindings(exports2,
|
||||
self.add_exports_of_namebindings(&mut *exports2,
|
||||
*ident,
|
||||
*namebindings,
|
||||
TypeNS,
|
||||
false);
|
||||
self.add_exports_of_namebindings(exports2,
|
||||
self.add_exports_of_namebindings(&mut *exports2,
|
||||
*ident,
|
||||
*namebindings,
|
||||
ValueNS,
|
||||
|
@ -3489,7 +3489,7 @@ impl Resolver {
|
|||
Some(target) => {
|
||||
debug!("(computing exports) maybe reexport '%s'",
|
||||
self.session.str_of(*ident));
|
||||
self.add_exports_of_namebindings(exports2,
|
||||
self.add_exports_of_namebindings(&mut *exports2,
|
||||
*ident,
|
||||
target.bindings,
|
||||
*ns,
|
||||
|
|
|
@ -901,26 +901,32 @@ fn collect_record_or_struct_fields(bcx: block, m: &[@Match], col: uint) ->
|
|||
}
|
||||
}
|
||||
|
||||
fn root_pats_as_necessary(bcx: block, m: &[@Match],
|
||||
col: uint, val: ValueRef)
|
||||
{
|
||||
fn root_pats_as_necessary(bcx: block,
|
||||
m: &[@Match],
|
||||
col: uint,
|
||||
val: ValueRef)
|
||||
-> block {
|
||||
let mut bcx = bcx;
|
||||
for vec::each(m) |br| {
|
||||
let pat_id = br.pats[col].id;
|
||||
|
||||
match bcx.ccx().maps.root_map.find({id:pat_id, derefs:0u}) {
|
||||
None => (),
|
||||
Some(scope_id) => {
|
||||
Some(root_info) => {
|
||||
// Note: the scope_id will always be the id of the match. See
|
||||
// the extended comment in rustc::middle::borrowck::preserve()
|
||||
// for details (look for the case covering cat_discr).
|
||||
|
||||
let datum = Datum {val: val, ty: node_id_type(bcx, pat_id),
|
||||
mode: ByRef, source: FromLvalue};
|
||||
datum.root(bcx, scope_id);
|
||||
return; // if we kept going, we'd only re-root the same value
|
||||
bcx = datum.root(bcx, root_info);
|
||||
// If we kept going, we'd only re-root the same value, so
|
||||
// return now.
|
||||
return bcx;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bcx;
|
||||
}
|
||||
|
||||
// Macro for deciding whether any of the remaining matches fit a given kind of
|
||||
|
@ -1243,7 +1249,7 @@ fn compile_submatch(bcx: block,
|
|||
if pat_id == 0 { pat_id = br.pats[col].id; }
|
||||
}
|
||||
|
||||
root_pats_as_necessary(bcx, m, col, val);
|
||||
bcx = root_pats_as_necessary(bcx, m, col, val);
|
||||
|
||||
let rec_fields = collect_record_or_struct_fields(bcx, m, col);
|
||||
if rec_fields.len() > 0 {
|
||||
|
|
|
@ -40,6 +40,7 @@ use lib;
|
|||
use metadata::common::link_meta;
|
||||
use metadata::{csearch, cstore, decoder, encoder};
|
||||
use middle::astencode;
|
||||
use middle::borrowck::RootInfo;
|
||||
use middle::pat_util::*;
|
||||
use middle::resolve;
|
||||
use middle::trans::_match;
|
||||
|
@ -963,15 +964,28 @@ fn get_landing_pad(bcx: block) -> BasicBlockRef {
|
|||
// block, so an SSA value that is valid in the inner block may not be valid in
|
||||
// the outer block. In fact, the inner block may not even execute. Rather
|
||||
// than generate the full SSA form, we just use an alloca'd value.
|
||||
fn add_root_cleanup(bcx: block, scope_id: ast::node_id,
|
||||
root_loc: ValueRef, ty: ty::t) {
|
||||
fn add_root_cleanup(bcx: block,
|
||||
root_info: RootInfo,
|
||||
root_loc: ValueRef,
|
||||
ty: ty::t) {
|
||||
|
||||
debug!("add_root_cleanup(bcx=%s, scope_id=%d, root_loc=%s, ty=%s)",
|
||||
bcx.to_str(), scope_id, val_str(bcx.ccx().tn, root_loc),
|
||||
debug!("add_root_cleanup(bcx=%s, \
|
||||
scope=%d, \
|
||||
freezes=%?, \
|
||||
root_loc=%s, \
|
||||
ty=%s)",
|
||||
bcx.to_str(),
|
||||
root_info.scope,
|
||||
root_info.freezes,
|
||||
val_str(bcx.ccx().tn, root_loc),
|
||||
ppaux::ty_to_str(bcx.ccx().tcx, ty));
|
||||
|
||||
let bcx_scope = find_bcx_for_scope(bcx, scope_id);
|
||||
let bcx_scope = find_bcx_for_scope(bcx, root_info.scope);
|
||||
if root_info.freezes {
|
||||
add_clean_frozen_root(bcx_scope, root_loc, ty);
|
||||
} else {
|
||||
add_clean_temp_mem(bcx_scope, root_loc, ty);
|
||||
}
|
||||
|
||||
fn find_bcx_for_scope(bcx: block, scope_id: ast::node_id) -> block {
|
||||
let mut bcx_sid = bcx;
|
||||
|
@ -1262,7 +1276,8 @@ fn trans_block_cleanups_(bcx: block,
|
|||
// In the last argument, Some(block) mean jump to this block, and none means
|
||||
// this is a landing pad and leaving should be accomplished with a resume
|
||||
// instruction.
|
||||
fn cleanup_and_leave(bcx: block, upto: Option<BasicBlockRef>,
|
||||
fn cleanup_and_leave(bcx: block,
|
||||
upto: Option<BasicBlockRef>,
|
||||
leave: Option<BasicBlockRef>) {
|
||||
let _icx = bcx.insn_ctxt("cleanup_and_leave");
|
||||
let mut cur = bcx, bcx = bcx;
|
||||
|
@ -1992,7 +2007,7 @@ fn trans_enum_def(ccx: @crate_ctxt, enum_definition: ast::enum_def,
|
|||
degen,
|
||||
path,
|
||||
vi,
|
||||
i);
|
||||
&mut *i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ use lib::llvm::{ValueRef, TypeRef, BasicBlockRef, BuilderRef, ModuleRef};
|
|||
use libc::{c_uint, c_int};
|
||||
use middle::trans::common::*;
|
||||
|
||||
use core::cast::transmute;
|
||||
use core::cast;
|
||||
use core::libc;
|
||||
use core::str;
|
||||
|
@ -24,6 +25,18 @@ use core::vec;
|
|||
use std::map::HashMap;
|
||||
use syntax::codemap;
|
||||
|
||||
fn terminate(cx: block, _: &str) {
|
||||
unsafe {
|
||||
cx.terminated = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_not_terminated(cx: block) {
|
||||
if cx.terminated {
|
||||
fail ~"already terminated!";
|
||||
}
|
||||
}
|
||||
|
||||
fn B(cx: block) -> BuilderRef {
|
||||
unsafe {
|
||||
let b = cx.fcx.ccx.builder.B;
|
||||
|
@ -86,8 +99,8 @@ fn count_insn(cx: block, category: &str) {
|
|||
fn RetVoid(cx: block) {
|
||||
unsafe {
|
||||
if cx.unreachable { return; }
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
check_not_terminated(cx);
|
||||
terminate(cx, "RetVoid");
|
||||
count_insn(cx, "retvoid");
|
||||
llvm::LLVMBuildRetVoid(B(cx));
|
||||
}
|
||||
|
@ -96,8 +109,8 @@ fn RetVoid(cx: block) {
|
|||
fn Ret(cx: block, V: ValueRef) {
|
||||
unsafe {
|
||||
if cx.unreachable { return; }
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
check_not_terminated(cx);
|
||||
terminate(cx, "Ret");
|
||||
count_insn(cx, "ret");
|
||||
llvm::LLVMBuildRet(B(cx), V);
|
||||
}
|
||||
|
@ -105,8 +118,8 @@ fn Ret(cx: block, V: ValueRef) {
|
|||
|
||||
fn AggregateRet(cx: block, RetVals: ~[ValueRef]) {
|
||||
if cx.unreachable { return; }
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
check_not_terminated(cx);
|
||||
terminate(cx, "AggregateRet");
|
||||
unsafe {
|
||||
llvm::LLVMBuildAggregateRet(B(cx), vec::raw::to_ptr(RetVals),
|
||||
RetVals.len() as c_uint);
|
||||
|
@ -116,8 +129,8 @@ fn AggregateRet(cx: block, RetVals: ~[ValueRef]) {
|
|||
fn Br(cx: block, Dest: BasicBlockRef) {
|
||||
unsafe {
|
||||
if cx.unreachable { return; }
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
check_not_terminated(cx);
|
||||
terminate(cx, "Br");
|
||||
count_insn(cx, "br");
|
||||
llvm::LLVMBuildBr(B(cx), Dest);
|
||||
}
|
||||
|
@ -127,8 +140,8 @@ fn CondBr(cx: block, If: ValueRef, Then: BasicBlockRef,
|
|||
Else: BasicBlockRef) {
|
||||
unsafe {
|
||||
if cx.unreachable { return; }
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
check_not_terminated(cx);
|
||||
terminate(cx, "CondBr");
|
||||
count_insn(cx, "condbr");
|
||||
llvm::LLVMBuildCondBr(B(cx), If, Then, Else);
|
||||
}
|
||||
|
@ -138,8 +151,8 @@ fn Switch(cx: block, V: ValueRef, Else: BasicBlockRef, NumCases: uint)
|
|||
-> ValueRef {
|
||||
unsafe {
|
||||
if cx.unreachable { return _Undef(V); }
|
||||
assert !cx.terminated;
|
||||
cx.terminated = true;
|
||||
check_not_terminated(cx);
|
||||
terminate(cx, "Switch");
|
||||
return llvm::LLVMBuildSwitch(B(cx), V, Else, NumCases as c_uint);
|
||||
}
|
||||
}
|
||||
|
@ -154,8 +167,8 @@ fn AddCase(S: ValueRef, OnVal: ValueRef, Dest: BasicBlockRef) {
|
|||
fn IndirectBr(cx: block, Addr: ValueRef, NumDests: uint) {
|
||||
unsafe {
|
||||
if cx.unreachable { return; }
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
check_not_terminated(cx);
|
||||
terminate(cx, "IndirectBr");
|
||||
count_insn(cx, "indirectbr");
|
||||
llvm::LLVMBuildIndirectBr(B(cx), Addr, NumDests as c_uint);
|
||||
}
|
||||
|
@ -171,8 +184,8 @@ fn noname() -> *libc::c_char unsafe {
|
|||
fn Invoke(cx: block, Fn: ValueRef, Args: ~[ValueRef],
|
||||
Then: BasicBlockRef, Catch: BasicBlockRef) {
|
||||
if cx.unreachable { return; }
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
check_not_terminated(cx);
|
||||
terminate(cx, "Invoke");
|
||||
debug!("Invoke(%s with arguments (%s))",
|
||||
val_str(cx.ccx().tn, Fn),
|
||||
str::connect(vec::map(Args, |a| val_str(cx.ccx().tn, *a)),
|
||||
|
@ -188,8 +201,8 @@ fn Invoke(cx: block, Fn: ValueRef, Args: ~[ValueRef],
|
|||
fn FastInvoke(cx: block, Fn: ValueRef, Args: ~[ValueRef],
|
||||
Then: BasicBlockRef, Catch: BasicBlockRef) {
|
||||
if cx.unreachable { return; }
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
check_not_terminated(cx);
|
||||
terminate(cx, "FastInvoke");
|
||||
unsafe {
|
||||
count_insn(cx, "fastinvoke");
|
||||
let v = llvm::LLVMBuildInvoke(B(cx), Fn, vec::raw::to_ptr(Args),
|
||||
|
@ -985,7 +998,8 @@ fn Trap(cx: block) {
|
|||
fn LandingPad(cx: block, Ty: TypeRef, PersFn: ValueRef,
|
||||
NumClauses: uint) -> ValueRef {
|
||||
unsafe {
|
||||
assert !cx.terminated && !cx.unreachable;
|
||||
check_not_terminated(cx);
|
||||
assert !cx.unreachable;
|
||||
count_insn(cx, "landingpad");
|
||||
return llvm::LLVMBuildLandingPad(B(cx), Ty, PersFn,
|
||||
NumClauses as c_uint, noname());
|
||||
|
@ -1001,8 +1015,8 @@ fn SetCleanup(cx: block, LandingPad: ValueRef) {
|
|||
|
||||
fn Resume(cx: block, Exn: ValueRef) -> ValueRef {
|
||||
unsafe {
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
check_not_terminated(cx);
|
||||
terminate(cx, "Resume");
|
||||
count_insn(cx, "resume");
|
||||
return llvm::LLVMBuildResume(B(cx), Exn);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ use middle::trans::build;
|
|||
use middle::trans::callee;
|
||||
use middle::trans::datum;
|
||||
use middle::trans::debuginfo;
|
||||
use middle::trans::expr;
|
||||
use middle::trans::glue;
|
||||
use middle::trans::meth;
|
||||
use middle::trans::reachable;
|
||||
|
@ -442,6 +443,31 @@ fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) {
|
|||
scope_clean_changed(scope_info);
|
||||
}
|
||||
}
|
||||
fn add_clean_frozen_root(bcx: block, val: ValueRef, t: ty::t) {
|
||||
debug!("add_clean_frozen_root(%s, %s, %s)",
|
||||
bcx.to_str(), val_str(bcx.ccx().tn, val),
|
||||
ty_to_str(bcx.ccx().tcx, t));
|
||||
let {root, rooted} = root_for_cleanup(bcx, val, t);
|
||||
let cleanup_type = cleanup_type(bcx.tcx(), t);
|
||||
do in_scope_cx(bcx) |scope_info| {
|
||||
scope_info.cleanups.push(
|
||||
clean_temp(val, |bcx| {
|
||||
let bcx = callee::trans_rtcall_or_lang_call(
|
||||
bcx,
|
||||
bcx.tcx().lang_items.return_to_mut_fn(),
|
||||
~[
|
||||
build::Load(bcx,
|
||||
build::PointerCast(bcx,
|
||||
root,
|
||||
T_ptr(T_ptr(T_i8()))))
|
||||
],
|
||||
expr::Ignore
|
||||
);
|
||||
glue::drop_ty_root(bcx, root, rooted, t)
|
||||
}, cleanup_type));
|
||||
scope_clean_changed(scope_info);
|
||||
}
|
||||
}
|
||||
fn add_clean_free(cx: block, ptr: ValueRef, heap: heap) {
|
||||
let free_fn = match heap {
|
||||
heap_shared => {
|
||||
|
|
|
@ -98,6 +98,7 @@
|
|||
use core::prelude::*;
|
||||
|
||||
use lib::llvm::ValueRef;
|
||||
use middle::borrowck::RootInfo;
|
||||
use middle::trans::base::*;
|
||||
use middle::trans::build::*;
|
||||
use middle::trans::common::*;
|
||||
|
@ -534,7 +535,7 @@ impl Datum {
|
|||
}
|
||||
}
|
||||
|
||||
fn root(bcx: block, scope_id: ast::node_id) {
|
||||
fn root(bcx: block, root_info: RootInfo) -> block {
|
||||
/*!
|
||||
*
|
||||
* In some cases, borrowck will decide that an @T/@[]/@str
|
||||
|
@ -542,18 +543,48 @@ impl Datum {
|
|||
* case, we will call this function, which will stash a copy
|
||||
* away until we exit the scope `scope_id`. */
|
||||
|
||||
debug!("root(scope_id=%?, self=%?)",
|
||||
scope_id, self.to_str(bcx.ccx()));
|
||||
debug!("root(scope_id=%?, freezes=%?, self=%?)",
|
||||
root_info.scope, root_info.freezes, self.to_str(bcx.ccx()));
|
||||
|
||||
if bcx.sess().trace() {
|
||||
trans_trace(
|
||||
bcx, None,
|
||||
fmt!("preserving until end of scope %d", scope_id));
|
||||
fmt!("preserving until end of scope %d", root_info.scope));
|
||||
}
|
||||
|
||||
let scratch = scratch_datum(bcx, self.ty, true);
|
||||
self.copy_to_datum(bcx, INIT, scratch);
|
||||
base::add_root_cleanup(bcx, scope_id, scratch.val, scratch.ty);
|
||||
base::add_root_cleanup(bcx, root_info, scratch.val, scratch.ty);
|
||||
|
||||
// If we need to freeze the box, do that now.
|
||||
if root_info.freezes {
|
||||
callee::trans_rtcall_or_lang_call(
|
||||
bcx,
|
||||
bcx.tcx().lang_items.borrow_as_imm_fn(),
|
||||
~[
|
||||
Load(bcx,
|
||||
PointerCast(bcx,
|
||||
scratch.val,
|
||||
T_ptr(T_ptr(T_i8()))))
|
||||
],
|
||||
expr::Ignore)
|
||||
} else {
|
||||
bcx
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_write_guard(bcx: block) -> block {
|
||||
// Create scratch space, but do not root it.
|
||||
let llval = match self.mode {
|
||||
ByValue => self.val,
|
||||
ByRef => Load(bcx, self.val),
|
||||
};
|
||||
|
||||
callee::trans_rtcall_or_lang_call(
|
||||
bcx,
|
||||
bcx.tcx().lang_items.check_not_borrowed_fn(),
|
||||
~[ PointerCast(bcx, llval, T_ptr(T_i8())) ],
|
||||
expr::Ignore)
|
||||
}
|
||||
|
||||
fn drop_val(bcx: block) -> block {
|
||||
|
@ -610,7 +641,7 @@ impl Datum {
|
|||
expr_id: ast::node_id, // id of expr being deref'd
|
||||
derefs: uint, // number of times deref'd already
|
||||
is_auto: bool) // if true, only deref if auto-derefable
|
||||
-> Option<Datum>
|
||||
-> (Option<Datum>, block)
|
||||
{
|
||||
let ccx = bcx.ccx();
|
||||
|
||||
|
@ -621,32 +652,39 @@ impl Datum {
|
|||
// root the autoderef'd value, if necessary:
|
||||
//
|
||||
// (Note: root'd values are always boxes)
|
||||
match ccx.maps.root_map.find({id:expr_id, derefs:derefs}) {
|
||||
None => (),
|
||||
Some(scope_id) => {
|
||||
self.root(bcx, scope_id);
|
||||
}
|
||||
}
|
||||
let key = {id:expr_id, derefs:derefs};
|
||||
let bcx = match ccx.maps.root_map.find(key) {
|
||||
None => bcx,
|
||||
Some(root_info) => self.root(bcx, root_info)
|
||||
};
|
||||
|
||||
// Perform the write guard, if necessary.
|
||||
//
|
||||
// (Note: write-guarded values are always boxes)
|
||||
let bcx = match ccx.maps.write_guard_map.find(key) {
|
||||
None => bcx,
|
||||
Some(_) => self.perform_write_guard(bcx)
|
||||
};
|
||||
|
||||
match ty::get(self.ty).sty {
|
||||
ty::ty_box(_) | ty::ty_uniq(_) => {
|
||||
return Some(self.box_body(bcx));
|
||||
return (Some(self.box_body(bcx)), bcx);
|
||||
}
|
||||
ty::ty_ptr(mt) => {
|
||||
if is_auto { // unsafe ptrs are not AUTO-derefable
|
||||
return None;
|
||||
return (None, bcx);
|
||||
} else {
|
||||
return Some(deref_ptr(bcx, &self, mt.ty));
|
||||
return (Some(deref_ptr(bcx, &self, mt.ty)), bcx);
|
||||
}
|
||||
}
|
||||
ty::ty_rptr(_, mt) => {
|
||||
return Some(deref_ptr(bcx, &self, mt.ty));
|
||||
return (Some(deref_ptr(bcx, &self, mt.ty)), bcx);
|
||||
}
|
||||
ty::ty_enum(did, ref substs) => {
|
||||
// Check whether this enum is a newtype enum:
|
||||
let variants = ty::enum_variants(ccx.tcx, did);
|
||||
if (*variants).len() != 1 || variants[0].args.len() != 1 {
|
||||
return None;
|
||||
return (None, bcx);
|
||||
}
|
||||
|
||||
let ty = ty::subst(ccx.tcx, substs, variants[0].args[0]);
|
||||
|
@ -655,12 +693,15 @@ impl Datum {
|
|||
// Recast lv.val as a pointer to the newtype
|
||||
// rather than a ptr to the enum type.
|
||||
let llty = T_ptr(type_of::type_of(ccx, ty));
|
||||
(
|
||||
Some(Datum {
|
||||
val: PointerCast(bcx, self.val, llty),
|
||||
ty: ty,
|
||||
mode: ByRef,
|
||||
source: FromLvalue
|
||||
})
|
||||
}),
|
||||
bcx
|
||||
)
|
||||
}
|
||||
ByValue => {
|
||||
// Actually, this case cannot happen right
|
||||
|
@ -672,7 +713,7 @@ impl Datum {
|
|||
// code in place here to do the right
|
||||
// thing if this change ever goes through.
|
||||
assert ty::type_is_immediate(ty);
|
||||
Some(Datum {ty: ty, ..self})
|
||||
(Some(Datum {ty: ty, ..self}), bcx)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -681,7 +722,7 @@ impl Datum {
|
|||
let fields = ty::struct_fields(ccx.tcx, did, substs);
|
||||
if fields.len() != 1 || fields[0].ident !=
|
||||
special_idents::unnamed_field {
|
||||
return None;
|
||||
return (None, bcx);
|
||||
}
|
||||
|
||||
let ty = fields[0].mt.ty;
|
||||
|
@ -691,12 +732,15 @@ impl Datum {
|
|||
// than a pointer to the struct type.
|
||||
// XXX: This isn't correct for structs with
|
||||
// destructors.
|
||||
(
|
||||
Some(Datum {
|
||||
val: GEPi(bcx, self.val, [0, 0, 0]),
|
||||
ty: ty,
|
||||
mode: ByRef,
|
||||
source: FromLvalue
|
||||
})
|
||||
}),
|
||||
bcx
|
||||
)
|
||||
}
|
||||
ByValue => {
|
||||
// Actually, this case cannot happen right now,
|
||||
|
@ -707,12 +751,12 @@ impl Datum {
|
|||
// code in place here to do the right thing if this
|
||||
// change ever goes through.
|
||||
assert ty::type_is_immediate(ty);
|
||||
Some(Datum {ty: ty, ..self})
|
||||
(Some(Datum {ty: ty, ..self}), bcx)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => { // not derefable.
|
||||
return None;
|
||||
return (None, bcx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -728,10 +772,11 @@ impl Datum {
|
|||
|
||||
fn deref(bcx: block,
|
||||
expr: @ast::expr, // the expression whose value is being deref'd
|
||||
derefs: uint) -> Datum {
|
||||
derefs: uint)
|
||||
-> DatumBlock {
|
||||
match self.try_deref(bcx, expr.id, derefs, false) {
|
||||
Some(lvres) => lvres,
|
||||
None => {
|
||||
(Some(lvres), bcx) => DatumBlock { bcx: bcx, datum: lvres },
|
||||
(None, _) => {
|
||||
bcx.ccx().sess.span_bug(
|
||||
expr.span, ~"Cannot deref this expression");
|
||||
}
|
||||
|
@ -740,7 +785,8 @@ impl Datum {
|
|||
|
||||
fn autoderef(bcx: block,
|
||||
expr_id: ast::node_id,
|
||||
max: uint) -> Datum {
|
||||
max: uint)
|
||||
-> DatumBlock {
|
||||
let _icx = bcx.insn_ctxt("autoderef");
|
||||
|
||||
debug!("autoderef(expr_id=%d, max=%?, self=%?)",
|
||||
|
@ -749,12 +795,14 @@ impl Datum {
|
|||
|
||||
let mut datum = self;
|
||||
let mut derefs = 0u;
|
||||
let mut bcx = bcx;
|
||||
while derefs < max {
|
||||
derefs += 1u;
|
||||
match datum.try_deref(bcx, expr_id, derefs, true) {
|
||||
None => break,
|
||||
Some(datum_deref) => {
|
||||
(None, new_bcx) => { bcx = new_bcx; break }
|
||||
(Some(datum_deref), new_bcx) => {
|
||||
datum = datum_deref;
|
||||
bcx = new_bcx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -763,7 +811,7 @@ impl Datum {
|
|||
// in which case we should have, or we asked to deref as many
|
||||
// times as we can
|
||||
assert derefs == max || max == uint::max_value;
|
||||
datum
|
||||
DatumBlock { bcx: bcx, datum: datum }
|
||||
}
|
||||
|
||||
fn get_base_and_len(bcx: block) -> (ValueRef, ValueRef) {
|
||||
|
|
|
@ -202,7 +202,10 @@ fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
|
|||
});
|
||||
|
||||
if adj.autoderefs > 0 {
|
||||
datum = datum.autoderef(bcx, expr.id, adj.autoderefs);
|
||||
let DatumBlock { bcx: new_bcx, datum: new_datum } =
|
||||
datum.autoderef(bcx, expr.id, adj.autoderefs);
|
||||
datum = new_datum;
|
||||
bcx = new_bcx;
|
||||
}
|
||||
|
||||
datum = match adj.autoref {
|
||||
|
@ -755,8 +758,8 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
|||
// the lvalue in there, and then arrange for it to be cleaned up
|
||||
// at the end of the scope with id `scope_id`:
|
||||
let root_key = {id:expr.id, derefs:0u};
|
||||
for bcx.ccx().maps.root_map.find(root_key).each |scope_id| {
|
||||
unrooted_datum.root(bcx, *scope_id);
|
||||
for bcx.ccx().maps.root_map.find(root_key).each |&root_info| {
|
||||
bcx = unrooted_datum.root(bcx, root_info);
|
||||
}
|
||||
|
||||
return DatumBlock {bcx: bcx, datum: unrooted_datum};
|
||||
|
@ -779,8 +782,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
|||
}
|
||||
ast::expr_unary(ast::deref, base) => {
|
||||
let basedatum = unpack_datum!(bcx, trans_to_datum(bcx, base));
|
||||
let derefdatum = basedatum.deref(bcx, base, 0);
|
||||
return DatumBlock {bcx: bcx, datum: derefdatum};
|
||||
return basedatum.deref(bcx, base, 0);
|
||||
}
|
||||
_ => {
|
||||
bcx.tcx().sess.span_bug(
|
||||
|
|
|
@ -74,7 +74,7 @@ fn add_u16(dest: &mut ~[u8], val: u16) {
|
|||
}
|
||||
|
||||
fn add_substr(dest: &mut ~[u8], src: ~[u8]) {
|
||||
add_u16(dest, vec::len(src) as u16);
|
||||
add_u16(&mut *dest, vec::len(src) as u16);
|
||||
*dest += src;
|
||||
}
|
||||
|
||||
|
|
|
@ -2206,8 +2206,15 @@ fn type_kind_ext(cx: ctxt, ty: t, allow_ty_var: bool) -> Kind {
|
|||
ty_rptr(re_static, mt) =>
|
||||
kind_safe_for_default_mode() | mutable_type_kind(cx, mt),
|
||||
|
||||
ty_rptr(_, mt) => {
|
||||
if mt.mutbl == ast::m_mutbl {
|
||||
// Mutable region pointers are noncopyable
|
||||
kind_noncopyable()
|
||||
} else {
|
||||
// General region pointers are copyable but NOT owned nor sendable
|
||||
ty_rptr(_, _) => kind_safe_for_default_mode(),
|
||||
kind_safe_for_default_mode()
|
||||
}
|
||||
}
|
||||
|
||||
// Unique boxes and vecs have the kind of their contained type,
|
||||
// but unique boxes can't be implicitly copyable.
|
||||
|
|
|
@ -2720,9 +2720,9 @@ fn check_enum_variants(ccx: @crate_ctxt,
|
|||
sp,
|
||||
/*bad*/copy vs,
|
||||
id,
|
||||
disr_vals,
|
||||
disr_val,
|
||||
variants);
|
||||
&mut *disr_vals,
|
||||
&mut *disr_val,
|
||||
&mut *variants);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -721,7 +721,7 @@ impl CoherenceChecker {
|
|||
debug!(
|
||||
"(creating impl) adding provided method `%s` to impl",
|
||||
sess.str_of(provided_method.method_info.ident));
|
||||
vec::push(all_methods, provided_method.method_info);
|
||||
vec::push(&mut *all_methods, provided_method.method_info);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1278,7 +1278,7 @@ impl RegionVarBindings {
|
|||
|
||||
return (move graph);
|
||||
|
||||
fn insert_edge(graph: &mut Graph,
|
||||
fn insert_edge(+graph: &mut Graph,
|
||||
node_id: RegionVid,
|
||||
edge_dir: Direction,
|
||||
edge_idx: uint) {
|
||||
|
|
|
@ -430,9 +430,9 @@ impl<T: Const Owned> &RWWriteMode<T> {
|
|||
/// Access the pre-downgrade RWARC in write mode.
|
||||
fn write<U>(blk: fn(x: &mut T) -> U) -> U {
|
||||
match *self {
|
||||
RWWriteMode((data, ref token, _)) => {
|
||||
RWWriteMode((ref data, ref token, _)) => {
|
||||
do token.write {
|
||||
blk(data)
|
||||
blk(&mut **data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -440,12 +440,14 @@ impl<T: Const Owned> &RWWriteMode<T> {
|
|||
/// Access the pre-downgrade RWARC in write mode with a condvar.
|
||||
fn write_cond<U>(blk: fn(x: &x/mut T, c: &c/Condvar) -> U) -> U {
|
||||
match *self {
|
||||
RWWriteMode((data, ref token, ref poison)) => {
|
||||
RWWriteMode((ref data, ref token, ref poison)) => {
|
||||
do token.write_cond |cond| {
|
||||
let cvar = Condvar {
|
||||
is_mutex: false, failed: poison.failed,
|
||||
cond: cond };
|
||||
blk(data, &cvar)
|
||||
is_mutex: false,
|
||||
failed: &mut *poison.failed,
|
||||
cond: cond
|
||||
};
|
||||
blk(&mut **data, &cvar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1322,7 +1322,12 @@ mod tests {
|
|||
fn of_string2() {
|
||||
let buf = @ mut ~"1234567890";
|
||||
let mut i = 0;
|
||||
while i < 10 { *buf = *buf + *buf; i+=1;}
|
||||
while i < 10 {
|
||||
let a = *buf;
|
||||
let b = *buf;
|
||||
*buf = a + b;
|
||||
i+=1;
|
||||
}
|
||||
let sample = @*buf;
|
||||
let r = of_str(sample);
|
||||
assert char_len(r) == str::char_len(*sample);
|
||||
|
@ -1353,7 +1358,12 @@ mod tests {
|
|||
fn iter1() {
|
||||
let buf = @ mut ~"1234567890";
|
||||
let mut i = 0;
|
||||
while i < 10 { *buf = *buf + *buf; i+=1;}
|
||||
while i < 10 {
|
||||
let a = *buf;
|
||||
let b = *buf;
|
||||
*buf = a + b;
|
||||
i+=1;
|
||||
}
|
||||
let sample = @*buf;
|
||||
let r = of_str(sample);
|
||||
|
||||
|
@ -1374,7 +1384,12 @@ mod tests {
|
|||
let init = @~"1234567890";
|
||||
let buf = @mut * init;
|
||||
let mut i = 0;
|
||||
while i < 8 { *buf = *buf + *buf; i+=1;}
|
||||
while i < 8 {
|
||||
let a = *buf;
|
||||
let b = *buf;
|
||||
*buf = a + b;
|
||||
i+=1;
|
||||
}
|
||||
let sample = @*buf;
|
||||
let r1 = of_str(sample);
|
||||
let mut r2 = of_str(init);
|
||||
|
|
|
@ -402,22 +402,22 @@ priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
|
|||
None => Err(~"Invalid year")
|
||||
},
|
||||
'c' => {
|
||||
parse_type(s, pos, 'a', tm)
|
||||
parse_type(s, pos, 'a', &mut *tm)
|
||||
.chain(|pos| parse_char(s, pos, ' '))
|
||||
.chain(|pos| parse_type(s, pos, 'b', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'b', &mut *tm))
|
||||
.chain(|pos| parse_char(s, pos, ' '))
|
||||
.chain(|pos| parse_type(s, pos, 'e', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'e', &mut *tm))
|
||||
.chain(|pos| parse_char(s, pos, ' '))
|
||||
.chain(|pos| parse_type(s, pos, 'T', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'T', &mut *tm))
|
||||
.chain(|pos| parse_char(s, pos, ' '))
|
||||
.chain(|pos| parse_type(s, pos, 'Y', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'Y', &mut *tm))
|
||||
}
|
||||
'D' | 'x' => {
|
||||
parse_type(s, pos, 'm', tm)
|
||||
parse_type(s, pos, 'm', &mut *tm)
|
||||
.chain(|pos| parse_char(s, pos, '/'))
|
||||
.chain(|pos| parse_type(s, pos, 'd', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'd', &mut *tm))
|
||||
.chain(|pos| parse_char(s, pos, '/'))
|
||||
.chain(|pos| parse_type(s, pos, 'y', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'y', &mut *tm))
|
||||
}
|
||||
'd' => match match_digits(s, pos, 2u, false) {
|
||||
Some(item) => { let (v, pos) = item; tm.tm_mday = v; Ok(pos) }
|
||||
|
@ -428,11 +428,11 @@ priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
|
|||
None => Err(~"Invalid day of the month")
|
||||
},
|
||||
'F' => {
|
||||
parse_type(s, pos, 'Y', tm)
|
||||
parse_type(s, pos, 'Y', &mut *tm)
|
||||
.chain(|pos| parse_char(s, pos, '-'))
|
||||
.chain(|pos| parse_type(s, pos, 'm', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'm', &mut *tm))
|
||||
.chain(|pos| parse_char(s, pos, '-'))
|
||||
.chain(|pos| parse_type(s, pos, 'd', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'd', &mut *tm))
|
||||
}
|
||||
'H' => {
|
||||
// FIXME (#2350): range check.
|
||||
|
@ -513,18 +513,18 @@ priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
|
|||
None => Err(~"Invalid hour")
|
||||
},
|
||||
'R' => {
|
||||
parse_type(s, pos, 'H', tm)
|
||||
parse_type(s, pos, 'H', &mut *tm)
|
||||
.chain(|pos| parse_char(s, pos, ':'))
|
||||
.chain(|pos| parse_type(s, pos, 'M', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'M', &mut *tm))
|
||||
}
|
||||
'r' => {
|
||||
parse_type(s, pos, 'I', tm)
|
||||
parse_type(s, pos, 'I', &mut *tm)
|
||||
.chain(|pos| parse_char(s, pos, ':'))
|
||||
.chain(|pos| parse_type(s, pos, 'M', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'M', &mut *tm))
|
||||
.chain(|pos| parse_char(s, pos, ':'))
|
||||
.chain(|pos| parse_type(s, pos, 'S', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'S', &mut *tm))
|
||||
.chain(|pos| parse_char(s, pos, ' '))
|
||||
.chain(|pos| parse_type(s, pos, 'p', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'p', &mut *tm))
|
||||
}
|
||||
'S' => {
|
||||
// FIXME (#2350): range check.
|
||||
|
@ -539,11 +539,11 @@ priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
|
|||
}
|
||||
//'s' {}
|
||||
'T' | 'X' => {
|
||||
parse_type(s, pos, 'H', tm)
|
||||
parse_type(s, pos, 'H', &mut *tm)
|
||||
.chain(|pos| parse_char(s, pos, ':'))
|
||||
.chain(|pos| parse_type(s, pos, 'M', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'M', &mut *tm))
|
||||
.chain(|pos| parse_char(s, pos, ':'))
|
||||
.chain(|pos| parse_type(s, pos, 'S', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'S', &mut *tm))
|
||||
}
|
||||
't' => parse_char(s, pos, '\t'),
|
||||
'u' => {
|
||||
|
@ -558,11 +558,11 @@ priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
|
|||
}
|
||||
}
|
||||
'v' => {
|
||||
parse_type(s, pos, 'e', tm)
|
||||
parse_type(s, pos, 'e', &mut *tm)
|
||||
.chain(|pos| parse_char(s, pos, '-'))
|
||||
.chain(|pos| parse_type(s, pos, 'b', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'b', &mut *tm))
|
||||
.chain(|pos| parse_char(s, pos, '-'))
|
||||
.chain(|pos| parse_type(s, pos, 'Y', tm))
|
||||
.chain(|pos| parse_type(s, pos, 'Y', &mut *tm))
|
||||
}
|
||||
//'W' {}
|
||||
'w' => {
|
||||
|
|
|
@ -160,7 +160,7 @@ fn consume_whitespace_counting_blank_lines(rdr: string_reader,
|
|||
comments: &mut ~[cmnt]) {
|
||||
while is_whitespace(rdr.curr) && !is_eof(rdr) {
|
||||
if rdr.col == CharPos(0u) && rdr.curr == '\n' {
|
||||
push_blank_line_comment(rdr, comments);
|
||||
push_blank_line_comment(rdr, &mut *comments);
|
||||
}
|
||||
bump(rdr);
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4d392c86feb6389f550d8110d36fa90d66c09251
|
||||
Subproject commit 1170ffba3ac5191930b40c897d4569a9d8a296a3
|
11
src/test/run-fail/write-guard-fail-2.rs
Normal file
11
src/test/run-fail/write-guard-fail-2.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
// error-pattern:borrowed
|
||||
|
||||
struct S {
|
||||
x: int
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = @mut S { x: 3 };
|
||||
let y: &S = x;
|
||||
x.x = 5;
|
||||
}
|
8
src/test/run-fail/write-guard-fail-3.rs
Normal file
8
src/test/run-fail/write-guard-fail-3.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
// error-pattern:borrowed
|
||||
|
||||
fn main() {
|
||||
let x = @mut 3;
|
||||
let y: &mut int = x;
|
||||
*x = 5;
|
||||
}
|
||||
|
13
src/test/run-fail/write-guard-fail.rs
Normal file
13
src/test/run-fail/write-guard-fail.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
// error-pattern:borrowed
|
||||
|
||||
fn f(x: &int, y: @mut int) {
|
||||
unsafe {
|
||||
*y = 2;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = @mut 3;
|
||||
f(x, x);
|
||||
}
|
||||
|
9
src/test/run-pass/write-guard.rs
Normal file
9
src/test/run-pass/write-guard.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
fn f(x: &int) {
|
||||
io::println(x.to_str());
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = @mut 3;
|
||||
f(x);
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue