diff --git a/src/libcore/f32.rs b/src/libcore/f32.rs index 4c1cc890de9e4..795a9f9371c3c 100644 --- a/src/libcore/f32.rs +++ b/src/libcore/f32.rs @@ -56,7 +56,6 @@ delegate!(fn exp2(n: c_float) -> c_float = cmath::c_float_utils::exp2) delegate!(fn abs(n: c_float) -> c_float = cmath::c_float_utils::abs) delegate!(fn abs_sub(a: c_float, b: c_float) -> c_float = cmath::c_float_utils::abs_sub) -delegate!(fn floor(n: c_float) -> c_float = cmath::c_float_utils::floor) delegate!(fn mul_add(a: c_float, b: c_float, c: c_float) -> c_float = cmath::c_float_utils::mul_add) delegate!(fn fmax(a: c_float, b: c_float) -> c_float = @@ -141,6 +140,10 @@ pub pure fn ge(x: f32, y: f32) -> bool { return x >= y; } #[inline(always)] pub pure fn gt(x: f32, y: f32) -> bool { return x > y; } +/// Returns `x` rounded down +#[inline(always)] +pub pure fn floor(x: f32) -> f32 { unsafe { floorf32(x) } } + // FIXME (#1999): replace the predicates below with llvm intrinsics or // calls to the libmath macros in the rust runtime for performance. @@ -298,6 +301,11 @@ impl f32: num::One { static pure fn one() -> f32 { 1.0 } } +#[abi="rust-intrinsic"] +pub extern { + fn floorf32(val: f32) -> f32; +} + // // Local Variables: // mode: rust diff --git a/src/libcore/f64.rs b/src/libcore/f64.rs index cbcdc2e81e6ec..8cd94c9357d61 100644 --- a/src/libcore/f64.rs +++ b/src/libcore/f64.rs @@ -57,7 +57,6 @@ delegate!(fn exp2(n: c_double) -> c_double = cmath::c_double_utils::exp2) delegate!(fn abs(n: c_double) -> c_double = cmath::c_double_utils::abs) delegate!(fn abs_sub(a: c_double, b: c_double) -> c_double = cmath::c_double_utils::abs_sub) -delegate!(fn floor(n: c_double) -> c_double = cmath::c_double_utils::floor) delegate!(fn mul_add(a: c_double, b: c_double, c: c_double) -> c_double = cmath::c_double_utils::mul_add) delegate!(fn fmax(a: c_double, b: c_double) -> c_double = @@ -210,12 +209,16 @@ pub pure fn is_infinite(x: f64) -> bool { return x == infinity || x == neg_infinity; } -/// Returns true if `x`is a finite number +/// Returns true if `x` is a finite number #[inline(always)] pub pure fn is_finite(x: f64) -> bool { return !(is_NaN(x) || is_infinite(x)); } +/// Returns `x` rounded down +#[inline(always)] +pub pure fn floor(x: f64) -> f64 { unsafe { floorf64(x) } } + // FIXME (#1999): add is_normal, is_subnormal, and fpclassify /* Module: consts */ @@ -322,6 +325,11 @@ impl f64: num::One { static pure fn one() -> f64 { 1.0 } } +#[abi="rust-intrinsic"] +pub extern { + fn floorf64(val: f64) -> f64; +} + // // Local Variables: // mode: rust diff --git a/src/libcore/managed.rs b/src/libcore/managed.rs index 8395526276c8e..301591acd09aa 100644 --- a/src/libcore/managed.rs +++ b/src/libcore/managed.rs @@ -14,9 +14,12 @@ #[forbid(deprecated_mode)]; #[forbid(deprecated_pattern)]; +use cast::transmute; use cmp::{Eq, Ord}; +use managed::raw::BoxRepr; use prelude::*; use ptr; +use rt::rt_fail_borrowed; pub mod raw { use intrinsic::TyDesc; @@ -35,12 +38,40 @@ pub mod raw { } +#[cfg(target_word_size = "32")] +const FROZEN_BIT: uint = 0x80000000; +#[cfg(target_word_size = "64")] +const FROZEN_BIT: uint = 0x8000000000000000; + #[inline(always)] pub pure fn ptr_eq(a: @T, b: @T) -> bool { //! Determine if two shared boxes point to the same object unsafe { ptr::addr_of(&(*a)) == ptr::addr_of(&(*b)) } } +#[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(); + } +} + #[cfg(notest)] impl @const T : Eq { #[inline(always)] diff --git a/src/libcore/rt.rs b/src/libcore/rt.rs index e0eb2fcfd83e9..6f9c0db9d0914 100644 --- a/src/libcore/rt.rs +++ b/src/libcore/rt.rs @@ -56,6 +56,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 { diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 030856539a5e8..59980b70dcb7a 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -292,7 +292,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)); @@ -309,7 +309,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, diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index eb9d4ae6a30ad..493d1c5c2777f 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -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 = @{ diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 3d5bdac596bc6..5635d5d18e59b 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -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( diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs index 52cbd1da10c9d..30ed831c53fe0 100644 --- a/src/librustc/middle/borrowck/gather_loans.rs +++ b/src/librustc/middle/borrowck/gather_loans.rs @@ -454,10 +454,16 @@ impl gather_loan_ctxt { let e = {cmt: cmt, code: err_mutbl(req_mutbl)}; if req_mutbl == m_imm { - // you can treat mutable things as imm if you are pure - debug!("imm required, must be pure"); - - Ok(pc_if_pure(e)) + // 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) } diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs index 92a3ce2458913..80e5dacae41e9 100644 --- a/src/librustc/middle/borrowck/loan.rs +++ b/src/librustc/middle/borrowck/loan.rs @@ -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( diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 19fc9eb175d60..91ea70f507ea7 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -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; +type root_map = HashMap; // 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; +// A set containing IDs of expressions of gc'd type that need to have a write +// guard. +type write_guard_map = HashMap; + // 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 - } } // ___________________________________________________________________________ diff --git a/src/librustc/middle/borrowck/preserve.rs b/src/librustc/middle/borrowck/preserve.rs index 600ede8c720ec..18090012f637c 100644 --- a/src/librustc/middle/borrowck/preserve.rs +++ b/src/librustc/middle/borrowck/preserve.rs @@ -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; @@ -177,14 +178,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) { @@ -323,8 +326,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 { + fn attempt_root(cmt: cmt, + base: cmt, + derefs: uint) + -> bckres { 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 @@ -349,21 +354,26 @@ 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 an *immutable* 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"); return Err({cmt:cmt, - code:err_out_of_root_scope(root_region, - self.scope_region)}); + code:err_out_of_root_scope(root_region, + self.scope_region)}); } } // we won't be able to root long enough _ => { return Err({cmt:cmt, - code:err_out_of_root_scope(root_region, - self.scope_region)}); + code:err_out_of_root_scope(root_region, + self.scope_region)}); } } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index ae56fb7f717da..0ed29c302e050 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -71,16 +71,19 @@ pub enum LangItem { ExchangeFreeFnLangItem, // 27 MallocFnLangItem, // 28 FreeFnLangItem, // 29 + BorrowAsImmFnLangItem, // 30 + ReturnToMutFnLangItem, // 31 + CheckNotBorrowedFnLangItem, // 32 } struct LanguageItems { - items: [ Option * 30 ] + items: [ Option * 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, diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index b72950cbf4b1b..5eb08429207dc 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -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 { 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 + } + } +} + diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index cc2a0ebcbd79c..d2105a6d442fa 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -909,14 +909,14 @@ fn root_pats_as_necessary(bcx: block, m: &[@Match], 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); + datum.root(bcx, root_info); return; // if we kept going, we'd only re-root the same value } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 40740be54cf5b..04b849d6b3837 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -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) { - - 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), +fn add_root_cleanup(bcx: block, + root_info: RootInfo, + root_loc: ValueRef, + ty: ty::t) { + + 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); - add_clean_temp_mem(bcx_scope, root_loc, ty); + 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, +fn cleanup_and_leave(bcx: block, + upto: Option, leave: Option) { let _icx = bcx.insn_ctxt("cleanup_and_leave"); let mut cur = bcx, bcx = bcx; diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs index dcf1b488d6fa7..beaf3b68617c1 100644 --- a/src/librustc/middle/trans/build.rs +++ b/src/librustc/middle/trans/build.rs @@ -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); } diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 5fd36228f7357..54e631dd2b28c 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -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 => |a| glue::trans_free(a, ptr), diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index b94d065859776..8c024b364c328 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -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 + -> (Option, 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 - }) + ( + 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 - }) + ( + 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) { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index c26328d7d9c3f..7cf0f4998241d 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -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 { @@ -754,8 +757,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}; @@ -778,8 +781,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( diff --git a/src/libuv b/src/libuv index 4d392c86feb63..1170ffba3ac51 160000 --- a/src/libuv +++ b/src/libuv @@ -1 +1 @@ -Subproject commit 4d392c86feb6389f550d8110d36fa90d66c09251 +Subproject commit 1170ffba3ac5191930b40c897d4569a9d8a296a3 diff --git a/src/test/bench/noise.rs b/src/test/bench/noise.rs new file mode 100644 index 0000000000000..a07dcee35f4d3 --- /dev/null +++ b/src/test/bench/noise.rs @@ -0,0 +1,110 @@ +// Perlin noise benchmark from https://gist.github.com/1170424 + +struct Vec2 { + x: f32, + y: f32, +} + +fn lerp(a: f32, b: f32, v: f32) -> f32 { a * (1.0 - v) + b * v } +fn smooth(v: f32) -> f32 { v * v * (3.0 - 2.0 * v) } + +fn random_gradient(r: rand::Rng) -> Vec2 { + let v = r.gen_float() * float::consts::pi * 2.0; + Vec2{ + x: float::cos(v) as f32, + y: float::sin(v) as f32, + } +} + +fn gradient(orig: Vec2, grad: Vec2, p: Vec2) -> f32 { + let sp = Vec2{x: p.x - orig.x, y: p.y - orig.y}; + grad.x * sp.x + grad.y + sp.y +} + +struct Noise2DContext { + rgradients: [Vec2 * 256], + permutations: [int * 256], +} + +fn Noise2DContext() -> ~Noise2DContext { + let r = rand::Rng(); + let mut rgradients = [ Vec2 { x: 0.0, y: 0.0 }, ..256 ]; + for int::range(0, 256) |i| { rgradients[i] = random_gradient(r); } + let mut permutations = [ 0, ..256 ]; + for int::range(0, 256) |i| { permutations[i] = i; } + r.shuffle_mut(permutations); + + ~Noise2DContext{ + rgradients: move rgradients, + permutations: move permutations, + } +} + +impl Noise2DContext { + #[inline(always)] + fn get_gradient(&self, x: int, y: int) -> Vec2 { + let idx = self.permutations[x & 255] + self.permutations[y & 255]; + self.rgradients[idx & 255] + } + + #[inline(always)] + fn get_gradients(&self, gradients: &mut [Vec2 * 4], origins: &mut [Vec2 * 4], x: f32, y: f32) { + let x0f = f32::floor(x); + let y0f = f32::floor(y); + let x0 = x0f as int; + let y0 = y0f as int; + let x1 = x0 + 1; + let y1 = y0 + 1; + + gradients[0] = self.get_gradient(x0, y0); + gradients[1] = self.get_gradient(x1, y0); + gradients[2] = self.get_gradient(x0, y1); + gradients[3] = self.get_gradient(x1, y1); + + origins[0] = Vec2{x: x0f + 0.0, y: y0f + 0.0}; + origins[1] = Vec2{x: x0f + 1.0, y: y0f + 0.0}; + origins[2] = Vec2{x: x0f + 0.0, y: y0f + 1.0}; + origins[3] = Vec2{x: x0f + 1.0, y: y0f + 1.0}; + } + + fn get(&self, x: f32, y: f32) -> f32 { + let p = Vec2{x: x, y: y}; + let mut gradients = [ Vec2 { x: 0.0, y: 0.0 }, ..4 ]; + let mut origins = [ Vec2 { x: 0.0, y: 0.0 }, ..4 ]; + self.get_gradients(&mut gradients, &mut origins, x, y); + let v0 = gradient(origins[0], gradients[0], p); + let v1 = gradient(origins[1], gradients[1], p); + let v2 = gradient(origins[2], gradients[2], p); + let v3 = gradient(origins[3], gradients[3], p); + let fx = smooth(x - origins[0].x); + let vx0 = lerp(v0, v1, fx); + let vx1 = lerp(v2, v3, fx); + let fy = smooth(y - origins[0].y); + lerp(vx0, vx1, fy) + } +} + +fn main() { + let symbols = [" ", "░", "▒", "▓", "█", "█"]; + let mut pixels = vec::from_elem(256*256, 0f32); + let n2d = Noise2DContext(); + for int::range(0, 100) |_| { + for int::range(0, 256) |y| { + for int::range(0, 256) |x| { + let v = n2d.get( + x as f32 * 0.1f32, + y as f32 * 0.1f32 + ) * 0.5f32 + 0.5f32; + pixels[y*256+x] = v; + }; + }; + }; + + /*for int::range(0, 256) |y| { + for int::range(0, 256) |x| { + io::print(symbols[pixels[y*256+x] / 0.2f32 as int]); + } + io::println(""); + }*/ +} + diff --git a/src/test/run-fail/write-guard-fail-2.rs b/src/test/run-fail/write-guard-fail-2.rs new file mode 100644 index 0000000000000..126135772ade0 --- /dev/null +++ b/src/test/run-fail/write-guard-fail-2.rs @@ -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; +} diff --git a/src/test/run-fail/write-guard-fail.rs b/src/test/run-fail/write-guard-fail.rs new file mode 100644 index 0000000000000..d393832c6e862 --- /dev/null +++ b/src/test/run-fail/write-guard-fail.rs @@ -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); +} + diff --git a/src/test/run-pass/write-guard.rs b/src/test/run-pass/write-guard.rs new file mode 100644 index 0000000000000..bc370b20d155b --- /dev/null +++ b/src/test/run-pass/write-guard.rs @@ -0,0 +1,9 @@ +fn f(x: &int) { + io::println(x.to_str()); +} + +fn main() { + let x = @mut 3; + f(x); +} +