1
Fork 0

librustc: Implement write guards for borrowing @mut to & or &mut. r=nmatsakis

This commit is contained in:
Patrick Walton 2013-01-11 21:01:42 -08:00
parent 8bde2c1d65
commit f405e41d7a
38 changed files with 522 additions and 365 deletions

View file

@ -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);
}

View file

@ -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 */

View file

@ -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;

View file

@ -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;

View file

@ -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)),

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
}
}
}

View file

@ -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,

View file

@ -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 = @{

View file

@ -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(

View file

@ -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)
}

View file

@ -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(

View file

@ -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
}
}
// ___________________________________________________________________________

View file

@ -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");

View file

@ -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,

View file

@ -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
}
}
}

View file

@ -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,

View file

@ -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 {

View file

@ -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);
}
}
}

View file

@ -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);
}

View file

@ -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 => {

View file

@ -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) {

View file

@ -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(

View file

@ -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;
}

View file

@ -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.

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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) {

View file

@ -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)
}
}
}

View file

@ -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);

View file

@ -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' => {

View file

@ -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

View 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;
}

View file

@ -0,0 +1,8 @@
// error-pattern:borrowed
fn main() {
let x = @mut 3;
let y: &mut int = x;
*x = 5;
}

View 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);
}

View file

@ -0,0 +1,9 @@
fn f(x: &int) {
io::println(x.to_str());
}
fn main() {
let x = @mut 3;
f(x);
}