From 0fade3a714f7a7f9bff5c11f9f37528d0ab168a1 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 13 Nov 2013 23:46:47 -0800 Subject: [PATCH 01/10] Introduce Mut to libstd Based off of blake2-ppc's work in #9429. --- src/libstd/lib.rs | 1 + src/libstd/mutable.rs | 298 ++++++++++++++++++++++++ src/test/compile-fail/mut-not-freeze.rs | 18 ++ 3 files changed, 317 insertions(+) create mode 100644 src/libstd/mutable.rs create mode 100644 src/test/compile-fail/mut-not-freeze.rs diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index ffa91df4e8a86..10887369b159f 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -164,6 +164,7 @@ pub mod result; pub mod either; pub mod hashmap; pub mod cell; +pub mod mutable; pub mod trie; diff --git a/src/libstd/mutable.rs b/src/libstd/mutable.rs new file mode 100644 index 0000000000000..63caa52d3af1d --- /dev/null +++ b/src/libstd/mutable.rs @@ -0,0 +1,298 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A mutable memory location with dynamically checked borrow rules + +use prelude::*; + +use cast; +use util::NonCopyable; + +/// A mutable memory location with dynamically checked borrow rules +#[no_freeze] +pub struct Mut { + priv value: T, + priv borrow: BorrowFlag, + priv nc: NonCopyable +} + +// Values [1, MAX-1] represent the number of `Ref` active +// (will not outgrow its range since `uint` is the size of the address space) +type BorrowFlag = uint; +static UNUSED: BorrowFlag = 0; +static WRITING: BorrowFlag = -1; + +impl Mut { + /// Create a new `Mut` containing `value` + pub fn new(value: T) -> Mut { + Mut { + value: value, + borrow: UNUSED, + nc: NonCopyable + } + } + + /// Consumes the `Mut`, returning the wrapped value. + pub fn unwrap(self) -> T { + assert!(self.borrow == UNUSED); + self.value + } + + unsafe fn as_mut<'a>(&'a self) -> &'a mut Mut { + cast::transmute_mut(self) + } + + /// Attempts to immutably borrow the wrapped value. + /// + /// The borrow lasts until the returned `Ref` exits scope. Multiple + /// immutable borrows can be taken out at the same time. + /// + /// Returns `None` if the value is currently mutably borrowed. + pub fn try_borrow<'a>(&'a self) -> Option> { + match self.borrow { + WRITING => None, + _ => { + unsafe { self.as_mut().borrow += 1; } + Some(Ref { parent: self }) + } + } + } + + /// Immutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `Ref` exits scope. Multiple + /// immutable borrows can be taken out at the same time. + /// + /// # Failure + /// + /// Fails if the value is currently mutably borrowed. + pub fn borrow<'a>(&'a self) -> Ref<'a, T> { + match self.try_borrow() { + Some(ptr) => ptr, + None => fail!("Mut already mutably borrowed") + } + } + + /// Mutably borrows the wrapped value. + /// + /// The borrow lasts untile the returned `MutRef` exits scope. The value + /// cannot be borrowed while this borrow is active. + /// + /// Returns `None` if the value is currently borrowed. + pub fn try_borrow_mut<'a>(&'a self) -> Option> { + match self.borrow { + UNUSED => unsafe { + let mut_self = self.as_mut(); + mut_self.borrow = WRITING; + Some(MutRef { parent: mut_self }) + }, + _ => None + } + } + + /// Mutably borrows the wrapped value. + /// + /// The borrow lasts untile the returned `MutRef` exits scope. The value + /// cannot be borrowed while this borrow is active. + /// + /// # Failure + /// + /// Fails if the value is currently borrowed. + pub fn borrow_mut<'a>(&'a self) -> MutRef<'a, T> { + match self.try_borrow_mut() { + Some(ptr) => ptr, + None => fail!("Mut already borrowed") + } + } + + /// Immutably borrows the wrapped value and applies `blk` to it. + /// + /// # Failure + /// + /// Fails if the value is currently mutably borrowed. + #[inline] + pub fn map(&self, blk: |&T| -> U) -> U { + let ptr = self.borrow(); + blk(ptr.get()) + } + + /// Mutably borrows the wrapped value and applies `blk` to it. + /// + /// # Failure + /// + /// Fails if the value is currently borrowed. + #[inline] + pub fn map_mut(&self, blk: |&mut T| -> U) -> U { + let mut ptr = self.borrow_mut(); + blk(ptr.get()) + } +} + +impl Clone for Mut { + fn clone(&self) -> Mut { + let x = self.borrow(); + Mut::new(x.get().clone()) + } +} + +impl DeepClone for Mut { + fn deep_clone(&self) -> Mut { + let x = self.borrow(); + Mut::new(x.get().deep_clone()) + } +} + +impl Eq for Mut { + fn eq(&self, other: &Mut) -> bool { + let a = self.borrow(); + let b = other.borrow(); + a.get() == b.get() + } +} + +/// Wraps a borrowed reference to a value in a `Mut` box. +pub struct Ref<'box, T> { + priv parent: &'box Mut +} + +#[unsafe_destructor] +impl<'box, T> Drop for Ref<'box, T> { + fn drop(&mut self) { + assert!(self.parent.borrow != WRITING && self.parent.borrow != UNUSED); + unsafe { self.parent.as_mut().borrow -= 1; } + } +} + +impl<'box, T> Ref<'box, T> { + /// Retrieve an immutable reference to the stored value. + #[inline] + pub fn get<'a>(&'a self) -> &'a T { + &self.parent.value + } +} + +/// Wraps a mutable borrowed reference to a value in a `Mut` box. +pub struct MutRef<'box, T> { + priv parent: &'box mut Mut +} + +#[unsafe_destructor] +impl<'box, T> Drop for MutRef<'box, T> { + fn drop(&mut self) { + assert!(self.parent.borrow == WRITING); + unsafe { self.parent.as_mut().borrow = UNUSED; } + } +} + +impl<'box, T> MutRef<'box, T> { + /// Retrieve a mutable reference to the stored value. + #[inline] + pub fn get<'a>(&'a mut self) -> &'a mut T { + &mut self.parent.value + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn double_imm_borrow() { + let x = Mut::new(0); + let _b1 = x.borrow(); + x.borrow(); + } + + #[test] + fn no_mut_then_imm_borrow() { + let x = Mut::new(0); + let _b1 = x.borrow_mut(); + assert!(x.try_borrow().is_none()); + } + + #[test] + fn no_imm_then_borrow_mut() { + let x = Mut::new(0); + let _b1 = x.borrow(); + assert!(x.try_borrow_mut().is_none()); + } + + #[test] + fn no_double_borrow_mut() { + let x = Mut::new(0); + let _b1 = x.borrow_mut(); + assert!(x.try_borrow_mut().is_none()); + } + + #[test] + fn imm_release_borrow_mut() { + let x = Mut::new(0); + { + let _b1 = x.borrow(); + } + x.borrow_mut(); + } + + #[test] + fn mut_release_borrow_mut() { + let x = Mut::new(0); + { + let _b1 = x.borrow_mut(); + } + x.borrow(); + } + + #[test] + fn double_borrow_single_release_no_borrow_mut() { + let x = Mut::new(0); + let _b1 = x.borrow(); + { + let _b2 = x.borrow(); + } + assert!(x.try_borrow_mut().is_none()); + } + + #[test] + fn map_ok() { + let x = Mut::new(0); + assert_eq!(1, x.map(|x| *x+1)); + } + + #[test] + #[should_fail] + fn mut_borrow_map() { + let x = Mut::new(0); + let _b1 = x.borrow_mut(); + x.map(|x| *x+1); + } + + #[test] + fn borrow_map() { + let x = Mut::new(0); + let _b1 = x.borrow(); + assert_eq!(1, x.map(|x| *x+1)); + } + + #[test] + fn map_mut_ok() { + let x = Mut::new(0); + x.map_mut(|x| *x += 1); + let b = x.borrow(); + assert_eq!(1, *b.get()); + } + + #[test] + #[should_fail] + fn borrow_map_mut() { + let x = Mut::new(0); + let _b = x.borrow(); + x.map_mut(|x| *x += 1); + } +} diff --git a/src/test/compile-fail/mut-not-freeze.rs b/src/test/compile-fail/mut-not-freeze.rs new file mode 100644 index 0000000000000..9a9669cbb3bbd --- /dev/null +++ b/src/test/compile-fail/mut-not-freeze.rs @@ -0,0 +1,18 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::mutable::Mut; + +fn f(_: T) {} + +fn main() { + let x = Mut::new(0); + f(x); //~ ERROR: which does not fulfill `Freeze` +} From 7c9daa8ff71cb5896af9bb9a6ec8e15391e76b4e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 14 Nov 2013 19:57:11 -0800 Subject: [PATCH 02/10] Remove RcMut Rc> should be used instead --- src/libstd/rc.rs | 237 ------------------ src/test/compile-fail/issue-7013.rs | 15 +- .../rcmut-not-const-and-not-owned.rs | 9 +- 3 files changed, 14 insertions(+), 247 deletions(-) diff --git a/src/libstd/rc.rs b/src/libstd/rc.rs index 9e83afa819c18..c4ee2190ad83b 100644 --- a/src/libstd/rc.rs +++ b/src/libstd/rc.rs @@ -14,10 +14,6 @@ The `Rc` type provides shared ownership of an immutable value. Destruction is de will occur as soon as the last owner is gone. It is marked as non-sendable because it avoids the overhead of atomic reference counting. -The `RcMut` type provides shared ownership of a mutable value. Since multiple owners prevent -inherited mutability, a dynamic freezing check is used to maintain the invariant that an `&mut` -reference is a unique handle and the type is marked as non-`Freeze`. - */ use ptr::RawPtr; @@ -151,236 +147,3 @@ mod test_rc { assert_eq!(**x.borrow(), 5); } } - -#[deriving(Eq)] -enum Borrow { - Mutable, - Immutable, - Nothing -} - -struct RcMutBox { - value: T, - count: uint, - borrow: Borrow -} - -/// Mutable reference counted pointer type -#[no_send] -#[no_freeze] -#[unsafe_no_drop_flag] -pub struct RcMut { - priv ptr: *mut RcMutBox, -} - -impl RcMut { - /// Construct a new mutable reference-counted box from a `Freeze` value - #[inline] - pub fn new(value: T) -> RcMut { - unsafe { RcMut::new_unchecked(value) } - } -} - -impl RcMut { - /// Construct a new mutable reference-counted box from a `Send` value - #[inline] - pub fn from_send(value: T) -> RcMut { - unsafe { RcMut::new_unchecked(value) } - } -} - -impl RcMut { - /// Unsafety construct a new mutable reference-counted box from any value. - /// - /// It is possible to create cycles, which will leak, and may interact - /// poorly with managed pointers. - #[inline] - pub unsafe fn new_unchecked(value: T) -> RcMut { - RcMut{ptr: transmute(~RcMutBox{value: value, count: 1, borrow: Nothing})} - } -} - -impl RcMut { - /// Fails if there is already a mutable borrow of the box - #[inline] - pub fn with_borrow(&self, f: |&T| -> U) -> U { - unsafe { - assert!((*self.ptr).borrow != Mutable); - let previous = (*self.ptr).borrow; - (*self.ptr).borrow = Immutable; - let res = f(&(*self.ptr).value); - (*self.ptr).borrow = previous; - res - } - } - - /// Fails if there is already a mutable or immutable borrow of the box - #[inline] - pub fn with_mut_borrow(&self, f: |&mut T| -> U) -> U { - unsafe { - assert_eq!((*self.ptr).borrow, Nothing); - (*self.ptr).borrow = Mutable; - let res = f(&mut (*self.ptr).value); - (*self.ptr).borrow = Nothing; - res - } - } -} - -#[unsafe_destructor] -impl Drop for RcMut { - fn drop(&mut self) { - unsafe { - if self.ptr.is_not_null() { - (*self.ptr).count -= 1; - if (*self.ptr).count == 0 { - let _: ~RcMutBox = transmute(self.ptr); - } - } - } - } -} - -impl Clone for RcMut { - /// Return a shallow copy of the reference counted pointer. - #[inline] - fn clone(&self) -> RcMut { - unsafe { - (*self.ptr).count += 1; - RcMut{ptr: self.ptr} - } - } -} - -impl DeepClone for RcMut { - /// Return a deep copy of the reference counted pointer. - #[inline] - fn deep_clone(&self) -> RcMut { - do self.with_borrow |x| { - // FIXME: #6497: should avoid freeze (slow) - unsafe { RcMut::new_unchecked(x.deep_clone()) } - } - } -} - -#[cfg(test)] -mod test_rc_mut { - use super::*; - - #[test] - fn test_clone() { - let x = RcMut::from_send(5); - let y = x.clone(); - do x.with_mut_borrow |value| { - *value = 20; - } - do y.with_borrow |value| { - assert_eq!(*value, 20); - } - } - - #[test] - fn test_deep_clone() { - let x = RcMut::new(5); - let y = x.deep_clone(); - do x.with_mut_borrow |value| { - *value = 20; - } - do y.with_borrow |value| { - assert_eq!(*value, 5); - } - } - - #[test] - fn borrow_many() { - let x = RcMut::from_send(5); - let y = x.clone(); - - do x.with_borrow |a| { - assert_eq!(*a, 5); - do y.with_borrow |b| { - assert_eq!(*b, 5); - do x.with_borrow |c| { - assert_eq!(*c, 5); - } - } - } - } - - #[test] - fn modify() { - let x = RcMut::new(5); - let y = x.clone(); - - do y.with_mut_borrow |a| { - assert_eq!(*a, 5); - *a = 6; - } - - do x.with_borrow |a| { - assert_eq!(*a, 6); - } - } - - #[test] - fn release_immutable() { - let x = RcMut::from_send(5); - do x.with_borrow |_| {} - do x.with_mut_borrow |_| {} - } - - #[test] - fn release_mutable() { - let x = RcMut::new(5); - do x.with_mut_borrow |_| {} - do x.with_borrow |_| {} - } - - #[test] - #[should_fail] - fn frozen() { - let x = RcMut::from_send(5); - let y = x.clone(); - - do x.with_borrow |_| { - do y.with_mut_borrow |_| { - } - } - } - - #[test] - #[should_fail] - fn mutable_dupe() { - let x = RcMut::new(5); - let y = x.clone(); - - do x.with_mut_borrow |_| { - do y.with_mut_borrow |_| { - } - } - } - - #[test] - #[should_fail] - fn mutable_freeze() { - let x = RcMut::from_send(5); - let y = x.clone(); - - do x.with_mut_borrow |_| { - do y.with_borrow |_| { - } - } - } - - #[test] - #[should_fail] - fn restore_freeze() { - let x = RcMut::new(5); - let y = x.clone(); - - do x.with_borrow |_| { - do x.with_borrow |_| {} - do y.with_mut_borrow |_| {} - } - } -} diff --git a/src/test/compile-fail/issue-7013.rs b/src/test/compile-fail/issue-7013.rs index 5f39aac19af0d..feea0765ec22b 100644 --- a/src/test/compile-fail/issue-7013.rs +++ b/src/test/compile-fail/issue-7013.rs @@ -8,21 +8,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::rc::RcMut; +use std::rc::Rc; +use std::mutable::Mut; trait Foo { - fn set(&mut self, v: RcMut); + fn set(&mut self, v: Rc>); } struct B { - v: Option> + v: Option>> } impl Foo for B { - fn set(&mut self, v: RcMut) + fn set(&mut self, v: Rc>) { self.v = Some(v); } @@ -36,7 +37,9 @@ struct A fn main() { let a = A {v: ~B{v: None} as ~Foo}; //~ ERROR cannot pack type `~B`, which does not fulfill `Send` - let v = RcMut::new(a); //~ ERROR instantiating a type parameter with an incompatible type + let v = Rc::from_send(Mut::new(a)); let w = v.clone(); - v.with_mut_borrow(|p| {p.v.set(w.clone());}) + let b = v.borrow(); + let mut b = b.borrow_mut(); + b.get().v.set(w.clone()); } diff --git a/src/test/compile-fail/rcmut-not-const-and-not-owned.rs b/src/test/compile-fail/rcmut-not-const-and-not-owned.rs index fce1f592c6268..981cec6f0ba19 100644 --- a/src/test/compile-fail/rcmut-not-const-and-not-owned.rs +++ b/src/test/compile-fail/rcmut-not-const-and-not-owned.rs @@ -8,13 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::rc::RcMut; +use std::mutable::Mut; +use std::rc::Rc; fn o(_: &T) {} fn c(_: &T) {} fn main() { - let x = RcMut::from_send(0); - o(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::RcMut`, which does not fulfill `Send` - c(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::RcMut`, which does not fulfill `Freeze` + let x = Rc::from_send(Mut::new(0)); + o(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::Rc>`, which does not fulfill `Send` + c(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::Rc>`, which does not fulfill `Freeze` } From 18119afbbefccae7d46a981ea11a7e1295fb7867 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 14 Nov 2013 23:25:00 -0800 Subject: [PATCH 03/10] Move Rc tests away from Cell --- src/libstd/rc.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libstd/rc.rs b/src/libstd/rc.rs index c4ee2190ad83b..2ffdf91ba2fd1 100644 --- a/src/libstd/rc.rs +++ b/src/libstd/rc.rs @@ -105,26 +105,26 @@ impl Drop for Rc { #[cfg(test)] mod test_rc { use super::*; - use cell::Cell; + use mutable::Mut; #[test] fn test_clone() { - let x = Rc::from_send(Cell::new(5)); + let x = Rc::from_send(Mut::new(5)); let y = x.clone(); - do x.borrow().with_mut_ref |inner| { + do x.borrow().map_mut |inner| { *inner = 20; } - assert_eq!(y.borrow().take(), 20); + assert_eq!(y.borrow().map(|v| *v), 20); } #[test] fn test_deep_clone() { - let x = Rc::from_send(Cell::new(5)); + let x = Rc::from_send(Mut::new(5)); let y = x.deep_clone(); - do x.borrow().with_mut_ref |inner| { + do x.borrow().map_mut |inner| { *inner = 20; } - assert_eq!(y.borrow().take(), 5); + assert_eq!(y.borrow().map(|v| *v), 5); } #[test] From 2e4bb2b9e9e10a665e23a34ae60652a90e9a1b82 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 15 Nov 2013 21:08:31 -0800 Subject: [PATCH 04/10] Cell -> Mut switch in comm --- src/libstd/rt/comm.rs | 44 +++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/libstd/rt/comm.rs b/src/libstd/rt/comm.rs index 3089ae2c2d0ce..077e9ba195bbc 100644 --- a/src/libstd/rt/comm.rs +++ b/src/libstd/rt/comm.rs @@ -22,11 +22,13 @@ use rt::select::{SelectInner, SelectPortInner}; use select::{Select, SelectPort}; use unstable::atomics::{AtomicUint, AtomicOption, Acquire, Relaxed, SeqCst}; use unstable::sync::UnsafeArc; +use util; use util::Void; use comm::{GenericChan, GenericSmartChan, GenericPort, Peekable, SendDeferred}; use cell::Cell; use clone::Clone; use tuple::ImmutableTuple; +use mutable::Mut; /// A combined refcount / BlockedTask-as-uint pointer. /// @@ -431,28 +433,28 @@ type StreamPortOne = PortOne>; /// A channel with unbounded size. pub struct Chan { - // FIXME #5372. Using Cell because we don't take &mut self - next: Cell> + // FIXME #5372. Using Mut because we don't take &mut self + next: Mut> } /// An port with unbounded size. pub struct Port { - // FIXME #5372. Using Cell because we don't take &mut self - next: Cell> + // FIXME #5372. Using Mut because we don't take &mut self + next: Mut>> } pub fn stream() -> (Port, Chan) { let (pone, cone) = oneshot(); - let port = Port { next: Cell::new(pone) }; - let chan = Chan { next: Cell::new(cone) }; + let port = Port { next: Mut::new(Some(pone)) }; + let chan = Chan { next: Mut::new(cone) }; return (port, chan); } impl Chan { fn try_send_inner(&self, val: T, do_resched: bool) -> bool { - let (next_pone, next_cone) = oneshot(); - let cone = self.next.take(); - self.next.put_back(next_cone); + let (next_pone, mut cone) = oneshot(); + let mut b = self.next.borrow_mut(); + util::swap(&mut cone, b.get()); cone.try_send_inner(StreamPayload { val: val, next: next_pone }, do_resched) } } @@ -489,10 +491,11 @@ impl GenericPort for Port { } fn try_recv(&self) -> Option { - do self.next.take_opt().map_default(None) |pone| { + let mut b = self.next.borrow_mut(); + do b.get().take().map_default(None) |pone| { match pone.try_recv() { Some(StreamPayload { val, next }) => { - self.next.put_back(next); + *b.get() = Some(next); Some(val) } None => None @@ -503,7 +506,7 @@ impl GenericPort for Port { impl Peekable for Port { fn peek(&self) -> bool { - self.next.with_mut_ref(|p| p.peek()) + self.next.map_mut(|p| p.get_mut_ref().peek()) } } @@ -514,18 +517,18 @@ impl Peekable for Port { impl<'self, T: Send> SelectInner for &'self Port { #[inline] fn optimistic_check(&mut self) -> bool { - do self.next.with_mut_ref |pone| { pone.optimistic_check() } + do self.next.map_mut |pone| { pone.get_mut_ref().optimistic_check() } } #[inline] fn block_on(&mut self, sched: &mut Scheduler, task: BlockedTask) -> bool { - let task = Cell::new(task); - do self.next.with_mut_ref |pone| { pone.block_on(sched, task.take()) } + let mut b = self.next.borrow_mut(); + b.get().get_mut_ref().block_on(sched, task) } #[inline] fn unblock_from(&mut self) -> bool { - do self.next.with_mut_ref |pone| { pone.unblock_from() } + do self.next.map_mut |pone| { pone.get_mut_ref().unblock_from() } } } @@ -552,9 +555,10 @@ impl Select for Port { } impl<'self, T: Send> SelectPortInner for &'self Port { fn recv_ready(self) -> Option { - match self.next.take().recv_ready() { + let mut b = self.next.borrow_mut(); + match b.get().take_unwrap().recv_ready() { Some(StreamPayload { val, next }) => { - self.next.put_back(next); + *b.get() = Some(next); Some(val) } None => None @@ -571,7 +575,7 @@ pub struct SharedChan { impl SharedChan { pub fn new(chan: Chan) -> SharedChan { - let next = chan.next.take(); + let next = chan.next.unwrap(); let next = AtomicOption::new(~next); SharedChan { next: UnsafeArc::new(next) } } @@ -625,7 +629,7 @@ pub struct SharedPort { impl SharedPort { pub fn new(port: Port) -> SharedPort { // Put the data port into a new link pipe - let next_data_port = port.next.take(); + let next_data_port = port.next.unwrap().unwrap(); let (next_link_port, next_link_chan) = oneshot(); next_link_chan.send(next_data_port); let next_link = AtomicOption::new(~next_link_port); From bb39cc3ae6db7effb17902d0cff0a737aef15101 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 15 Nov 2013 23:48:02 -0800 Subject: [PATCH 05/10] Make MutRef more consistent with &mut --- src/libstd/mutable.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libstd/mutable.rs b/src/libstd/mutable.rs index 63caa52d3af1d..c343e8734cb8c 100644 --- a/src/libstd/mutable.rs +++ b/src/libstd/mutable.rs @@ -82,16 +82,16 @@ impl Mut { /// Mutably borrows the wrapped value. /// - /// The borrow lasts untile the returned `MutRef` exits scope. The value + /// The borrow lasts untile the returned `RefMut` exits scope. The value /// cannot be borrowed while this borrow is active. /// /// Returns `None` if the value is currently borrowed. - pub fn try_borrow_mut<'a>(&'a self) -> Option> { + pub fn try_borrow_mut<'a>(&'a self) -> Option> { match self.borrow { UNUSED => unsafe { let mut_self = self.as_mut(); mut_self.borrow = WRITING; - Some(MutRef { parent: mut_self }) + Some(RefMut { parent: mut_self }) }, _ => None } @@ -99,13 +99,13 @@ impl Mut { /// Mutably borrows the wrapped value. /// - /// The borrow lasts untile the returned `MutRef` exits scope. The value + /// The borrow lasts untile the returned `RefMut` exits scope. The value /// cannot be borrowed while this borrow is active. /// /// # Failure /// /// Fails if the value is currently borrowed. - pub fn borrow_mut<'a>(&'a self) -> MutRef<'a, T> { + pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> { match self.try_borrow_mut() { Some(ptr) => ptr, None => fail!("Mut already borrowed") @@ -179,19 +179,19 @@ impl<'box, T> Ref<'box, T> { } /// Wraps a mutable borrowed reference to a value in a `Mut` box. -pub struct MutRef<'box, T> { +pub struct RefMut<'box, T> { priv parent: &'box mut Mut } #[unsafe_destructor] -impl<'box, T> Drop for MutRef<'box, T> { +impl<'box, T> Drop for RefMut<'box, T> { fn drop(&mut self) { assert!(self.parent.borrow == WRITING); - unsafe { self.parent.as_mut().borrow = UNUSED; } + self.parent.borrow = UNUSED; } } -impl<'box, T> MutRef<'box, T> { +impl<'box, T> RefMut<'box, T> { /// Retrieve a mutable reference to the stored value. #[inline] pub fn get<'a>(&'a mut self) -> &'a mut T { From 8a26266f6586f765ebdfbc0304e4976bfff28895 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 16 Nov 2013 11:19:25 -0800 Subject: [PATCH 06/10] Change Mut::map to Mut::with --- src/libstd/mutable.rs | 24 ++++++++++++------------ src/libstd/rc.rs | 8 ++++---- src/libstd/rt/comm.rs | 6 +++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/libstd/mutable.rs b/src/libstd/mutable.rs index c343e8734cb8c..98177b3cdf576 100644 --- a/src/libstd/mutable.rs +++ b/src/libstd/mutable.rs @@ -118,7 +118,7 @@ impl Mut { /// /// Fails if the value is currently mutably borrowed. #[inline] - pub fn map(&self, blk: |&T| -> U) -> U { + pub fn with(&self, blk: |&T| -> U) -> U { let ptr = self.borrow(); blk(ptr.get()) } @@ -129,7 +129,7 @@ impl Mut { /// /// Fails if the value is currently borrowed. #[inline] - pub fn map_mut(&self, blk: |&mut T| -> U) -> U { + pub fn with_mut(&self, blk: |&mut T| -> U) -> U { let mut ptr = self.borrow_mut(); blk(ptr.get()) } @@ -260,39 +260,39 @@ mod test { } #[test] - fn map_ok() { + fn with_ok() { let x = Mut::new(0); - assert_eq!(1, x.map(|x| *x+1)); + assert_eq!(1, x.with(|x| *x+1)); } #[test] #[should_fail] - fn mut_borrow_map() { + fn mut_borrow_with() { let x = Mut::new(0); let _b1 = x.borrow_mut(); - x.map(|x| *x+1); + x.with(|x| *x+1); } #[test] - fn borrow_map() { + fn borrow_with() { let x = Mut::new(0); let _b1 = x.borrow(); - assert_eq!(1, x.map(|x| *x+1)); + assert_eq!(1, x.with(|x| *x+1)); } #[test] - fn map_mut_ok() { + fn with_mut_ok() { let x = Mut::new(0); - x.map_mut(|x| *x += 1); + x.with_mut(|x| *x += 1); let b = x.borrow(); assert_eq!(1, *b.get()); } #[test] #[should_fail] - fn borrow_map_mut() { + fn borrow_with_mut() { let x = Mut::new(0); let _b = x.borrow(); - x.map_mut(|x| *x += 1); + x.with_mut(|x| *x += 1); } } diff --git a/src/libstd/rc.rs b/src/libstd/rc.rs index 2ffdf91ba2fd1..4cb2c79219482 100644 --- a/src/libstd/rc.rs +++ b/src/libstd/rc.rs @@ -111,20 +111,20 @@ mod test_rc { fn test_clone() { let x = Rc::from_send(Mut::new(5)); let y = x.clone(); - do x.borrow().map_mut |inner| { + do x.borrow().with_mut |inner| { *inner = 20; } - assert_eq!(y.borrow().map(|v| *v), 20); + assert_eq!(y.borrow().with(|v| *v), 20); } #[test] fn test_deep_clone() { let x = Rc::from_send(Mut::new(5)); let y = x.deep_clone(); - do x.borrow().map_mut |inner| { + do x.borrow().with_mut |inner| { *inner = 20; } - assert_eq!(y.borrow().map(|v| *v), 5); + assert_eq!(y.borrow().with(|v| *v), 5); } #[test] diff --git a/src/libstd/rt/comm.rs b/src/libstd/rt/comm.rs index 077e9ba195bbc..7441d0d3edcca 100644 --- a/src/libstd/rt/comm.rs +++ b/src/libstd/rt/comm.rs @@ -506,7 +506,7 @@ impl GenericPort for Port { impl Peekable for Port { fn peek(&self) -> bool { - self.next.map_mut(|p| p.get_mut_ref().peek()) + self.next.with_mut(|p| p.get_mut_ref().peek()) } } @@ -517,7 +517,7 @@ impl Peekable for Port { impl<'self, T: Send> SelectInner for &'self Port { #[inline] fn optimistic_check(&mut self) -> bool { - do self.next.map_mut |pone| { pone.get_mut_ref().optimistic_check() } + do self.next.with_mut |pone| { pone.get_mut_ref().optimistic_check() } } #[inline] @@ -528,7 +528,7 @@ impl<'self, T: Send> SelectInner for &'self Port { #[inline] fn unblock_from(&mut self) -> bool { - do self.next.map_mut |pone| { pone.get_mut_ref().unblock_from() } + do self.next.with_mut |pone| { pone.get_mut_ref().unblock_from() } } } From 5759f2fc571f9531c675a5966b44d570b7dbe685 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 16 Nov 2013 13:26:15 -0800 Subject: [PATCH 07/10] Strip down Cell functionality --- .../typeck/infer/region_inference/mod.rs | 28 ++++----- src/libstd/cell.rs | 62 +------------------ src/test/bench/msgsend-ring-mutex-arcs.rs | 9 ++- src/test/bench/msgsend-ring-rw-arcs.rs | 9 ++- 4 files changed, 24 insertions(+), 84 deletions(-) diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/typeck/infer/region_inference/mod.rs index ff4b20d369b6f..8af370792aee1 100644 --- a/src/librustc/middle/typeck/infer/region_inference/mod.rs +++ b/src/librustc/middle/typeck/infer/region_inference/mod.rs @@ -24,7 +24,6 @@ use middle::graph::{Direction, NodeIndex}; use util::common::indenter; use util::ppaux::{Repr}; -use std::cell::Cell; use std::hashmap::{HashMap, HashSet}; use std::uint; use std::vec; @@ -106,16 +105,15 @@ pub struct RegionVarBindings { undo_log: ~[UndoLogEntry], // This contains the results of inference. It begins as an empty - // cell and only acquires a value after inference is complete. - // We use a cell vs a mutable option to circumvent borrowck errors. - values: Cell<~[VarValue]>, + // option and only acquires a value after inference is complete. + values: Option<~[VarValue]>, } pub fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings { RegionVarBindings { tcx: tcx, var_origins: ~[], - values: Cell::new_empty(), + values: None, constraints: HashMap::new(), lubs: HashMap::new(), glbs: HashMap::new(), @@ -226,7 +224,7 @@ impl RegionVarBindings { constraint: Constraint, origin: SubregionOrigin) { // cannot add constraints once regions are resolved - assert!(self.values.is_empty()); + assert!(self.values.is_none()); debug!("RegionVarBindings: add_constraint({:?})", constraint); @@ -242,7 +240,7 @@ impl RegionVarBindings { sub: Region, sup: Region) { // cannot add constraints once regions are resolved - assert!(self.values.is_empty()); + assert!(self.values.is_none()); debug!("RegionVarBindings: make_subregion({:?}, {:?})", sub, sup); match (sub, sup) { @@ -277,7 +275,7 @@ impl RegionVarBindings { b: Region) -> Region { // cannot add constraints once regions are resolved - assert!(self.values.is_empty()); + assert!(self.values.is_none()); debug!("RegionVarBindings: lub_regions({:?}, {:?})", a, b); match (a, b) { @@ -300,7 +298,7 @@ impl RegionVarBindings { b: Region) -> Region { // cannot add constraints once regions are resolved - assert!(self.values.is_empty()); + assert!(self.values.is_none()); debug!("RegionVarBindings: glb_regions({:?}, {:?})", a, b); match (a, b) { @@ -319,14 +317,14 @@ impl RegionVarBindings { } pub fn resolve_var(&mut self, rid: RegionVid) -> ty::Region { - if self.values.is_empty() { - self.tcx.sess.span_bug( + let v = match self.values { + None => self.tcx.sess.span_bug( self.var_origins[rid.to_uint()].span(), format!("Attempt to resolve region variable before values have \ - been computed!")); - } + been computed!")), + Some(ref values) => values[rid.to_uint()] + }; - let v = self.values.with_ref(|values| values[rid.to_uint()]); debug!("RegionVarBindings: resolve_var({:?}={})={:?}", rid, rid.to_uint(), v); match v { @@ -482,7 +480,7 @@ impl RegionVarBindings { debug!("RegionVarBindings: resolve_regions()"); let mut errors = opt_vec::Empty; let v = self.infer_variable_values(&mut errors); - self.values.put_back(v); + self.values = Some(v); errors } } diff --git a/src/libstd/cell.rs b/src/libstd/cell.rs index 54849a44f6d3b..b33a18ed95dd3 100644 --- a/src/libstd/cell.rs +++ b/src/libstd/cell.rs @@ -8,12 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A mutable, nullable memory location +//! Runtime move semantics #[missing_doc]; use cast::transmute_mut; -use unstable::finally::Finally; use prelude::*; /* @@ -35,11 +34,6 @@ impl Cell { Cell { value: Some(value) } } - /// Creates a new empty cell with no value inside. - pub fn new_empty() -> Cell { - Cell { value: None } - } - /// Yields the value, failing if the cell is empty. pub fn take(&self) -> T { let this = unsafe { transmute_mut(self) }; @@ -56,34 +50,10 @@ impl Cell { this.value.take() } - /// Returns the value, failing if the cell is full. - pub fn put_back(&self, value: T) { - let this = unsafe { transmute_mut(self) }; - if !this.is_empty() { - fail!("attempt to put a value back into a full cell"); - } - this.value = Some(value); - } - /// Returns true if the cell is empty and false if the cell is full. pub fn is_empty(&self) -> bool { self.value.is_none() } - - /// Calls a closure with a reference to the value. - pub fn with_ref(&self, op: |v: &T| -> R) -> R { - do self.with_mut_ref |ptr| { op(ptr) } - } - - /// Calls a closure with a mutable reference to the value. - pub fn with_mut_ref(&self, op: |v: &mut T| -> R) -> R { - let mut v = Some(self.take()); - do (|| { - op(v.get_mut_ref()) - }).finally { - self.put_back(v.take_unwrap()); - } - } } #[test] @@ -93,38 +63,12 @@ fn test_basic() { let value = value_cell.take(); assert!(value == ~10); assert!(value_cell.is_empty()); - value_cell.put_back(value); - assert!(!value_cell.is_empty()); } #[test] #[should_fail] fn test_take_empty() { - let value_cell: Cell<~int> = Cell::new_empty(); + let value_cell: Cell<~int> = Cell::new(~0); + value_cell.take(); value_cell.take(); -} - -#[test] -#[should_fail] -fn test_put_back_non_empty() { - let value_cell = Cell::new(~10); - value_cell.put_back(~20); -} - -#[test] -fn test_with_ref() { - let good = 6; - let c = Cell::new(~[1, 2, 3, 4, 5, 6]); - let l = do c.with_ref() |v| { v.len() }; - assert_eq!(l, good); -} - -#[test] -fn test_with_mut_ref() { - let good = ~[1, 2, 3]; - let v = ~[1, 2]; - let c = Cell::new(v); - do c.with_mut_ref() |v| { v.push(3); } - let v = c.take(); - assert_eq!(v, good); } diff --git a/src/test/bench/msgsend-ring-mutex-arcs.rs b/src/test/bench/msgsend-ring-mutex-arcs.rs index e4d45c6b74a9a..f3b78ecd30c67 100644 --- a/src/test/bench/msgsend-ring-mutex-arcs.rs +++ b/src/test/bench/msgsend-ring-mutex-arcs.rs @@ -81,8 +81,7 @@ fn main() { let num_tasks = from_str::(args[1]).unwrap(); let msg_per_task = from_str::(args[2]).unwrap(); - let (num_chan, num_port) = init(); - let num_chan = Cell::new(num_chan); + let (mut num_chan, num_port) = init(); let start = time::precise_time_s(); @@ -92,7 +91,7 @@ fn main() { for i in range(1u, num_tasks) { //error!("spawning %?", i); let (new_chan, num_port) = init(); - let num_chan2 = Cell::new(num_chan.take()); + let num_chan2 = Cell::new(num_chan); let num_port = Cell::new(num_port); let new_future = do Future::spawn() { let num_chan = num_chan2.take(); @@ -100,11 +99,11 @@ fn main() { thread_ring(i, msg_per_task, num_chan, num_port1) }; futures.push(new_future); - num_chan.put_back(new_chan); + num_chan = new_chan; }; // do our iteration - thread_ring(0, msg_per_task, num_chan.take(), num_port); + thread_ring(0, msg_per_task, num_chan, num_port); // synchronize for f in futures.mut_iter() { diff --git a/src/test/bench/msgsend-ring-rw-arcs.rs b/src/test/bench/msgsend-ring-rw-arcs.rs index d326fd81518ee..47b1e5903280e 100644 --- a/src/test/bench/msgsend-ring-rw-arcs.rs +++ b/src/test/bench/msgsend-ring-rw-arcs.rs @@ -77,8 +77,7 @@ fn main() { let num_tasks = from_str::(args[1]).unwrap(); let msg_per_task = from_str::(args[2]).unwrap(); - let (num_chan, num_port) = init(); - let num_chan = Cell::new(num_chan); + let (mut num_chan, num_port) = init(); let start = time::precise_time_s(); @@ -88,7 +87,7 @@ fn main() { for i in range(1u, num_tasks) { //error!("spawning %?", i); let (new_chan, num_port) = init(); - let num_chan2 = Cell::new(num_chan.take()); + let num_chan2 = Cell::new(num_chan); let num_port = Cell::new(num_port); let new_future = do Future::spawn { let num_chan = num_chan2.take(); @@ -96,11 +95,11 @@ fn main() { thread_ring(i, msg_per_task, num_chan, num_port1) }; futures.push(new_future); - num_chan.put_back(new_chan); + num_chan = new_chan; }; // do our iteration - thread_ring(0, msg_per_task, num_chan.take(), num_port); + thread_ring(0, msg_per_task, num_chan, num_port); // synchronize for f in futures.mut_iter() { From 48cd8c646ace585153d6ed25baccdd8420742e42 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 16 Nov 2013 14:35:35 -0800 Subject: [PATCH 08/10] More Mut tests --- src/libstd/mutable.rs | 9 +++++++++ src/test/compile-fail/mut-cant-alias.rs | 18 +++++++++++++++++ .../compile-fail/mut-ptr-cant-outlive-ref.rs | 20 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/test/compile-fail/mut-cant-alias.rs create mode 100644 src/test/compile-fail/mut-ptr-cant-outlive-ref.rs diff --git a/src/libstd/mutable.rs b/src/libstd/mutable.rs index 98177b3cdf576..44efbc149c173 100644 --- a/src/libstd/mutable.rs +++ b/src/libstd/mutable.rs @@ -295,4 +295,13 @@ mod test { let _b = x.borrow(); x.with_mut(|x| *x += 1); } + + #[test] + #[should_fail] + fn discard_doesnt_unborrow() { + let x = Mut::new(0); + let _b = x.borrow(); + let _ = _b; + let _b = x.borrow_mut(); + } } diff --git a/src/test/compile-fail/mut-cant-alias.rs b/src/test/compile-fail/mut-cant-alias.rs new file mode 100644 index 0000000000000..8e37f88af8203 --- /dev/null +++ b/src/test/compile-fail/mut-cant-alias.rs @@ -0,0 +1,18 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::mutable::Mut; + +fn main() { + let m = Mut::new(0); + let mut b = m.borrow_mut(); + let b1 = b.get(); + let b2 = b.get(); //~ ERROR cannot borrow `b` as mutable more than once at a time +} diff --git a/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs b/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs new file mode 100644 index 0000000000000..0dbba87019aae --- /dev/null +++ b/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs @@ -0,0 +1,20 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::mutable::Mut; + +fn main() { + let m = Mut::new(0); + let p; + { + let b = m.borrow(); + p = b.get(); //~ ERROR borrowed value does not live long enough + } +} From c6ca9abcc651423fe85f522cbd20f1e64463c36f Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 16 Nov 2013 21:59:42 -0800 Subject: [PATCH 09/10] Add Rc::from_mut --- src/libstd/rc.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/libstd/rc.rs b/src/libstd/rc.rs index 4cb2c79219482..24e7decfb8235 100644 --- a/src/libstd/rc.rs +++ b/src/libstd/rc.rs @@ -21,6 +21,7 @@ use unstable::intrinsics::transmute; use ops::Drop; use kinds::{Freeze, Send}; use clone::{Clone, DeepClone}; +use mutable::Mut; struct RcBox { value: T, @@ -54,6 +55,16 @@ impl Rc { } } +impl Rc> { + /// Construct a new reference-counted box from a `Mut`-wrapped `Freeze` value + #[inline] + pub fn from_mut(value: Mut) -> Rc> { + unsafe { + Rc::new_unchecked(value) + } + } +} + impl Rc { /// Unsafety construct a new reference-counted box from any value. /// @@ -146,4 +157,10 @@ mod test_rc { let x = Rc::from_send(~5); assert_eq!(**x.borrow(), 5); } + + #[test] + fn test_from_mut() { + let a = 10; + let _x = Rc::from_mut(Mut::new(&a)); + } } From bdfaf04bd507bf99cff392c0a0c2df65ede96f69 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 21 Nov 2013 21:30:34 -0800 Subject: [PATCH 10/10] Move mutable::Mut to cell::RefCell --- src/libstd/cell.rs | 302 ++++++++++++++++- src/libstd/gc.rs | 14 +- src/libstd/lib.rs | 1 - src/libstd/mutable.rs | 307 ------------------ src/libstd/rc.rs | 16 +- src/libstd/rt/comm.rs | 15 +- src/test/compile-fail/issue-7013.rs | 10 +- src/test/compile-fail/mut-cant-alias.rs | 4 +- src/test/compile-fail/mut-not-freeze.rs | 4 +- .../compile-fail/mut-ptr-cant-outlive-ref.rs | 4 +- .../rcmut-not-const-and-not-owned.rs | 8 +- 11 files changed, 335 insertions(+), 350 deletions(-) delete mode 100644 src/libstd/mutable.rs diff --git a/src/libstd/cell.rs b/src/libstd/cell.rs index b33a18ed95dd3..019cd53be55bc 100644 --- a/src/libstd/cell.rs +++ b/src/libstd/cell.rs @@ -8,12 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Runtime move semantics +//! Types dealing with dynamic mutability #[missing_doc]; -use cast::transmute_mut; use prelude::*; +use cast; +use util::NonCopyable; + /* A dynamic, mutable location. @@ -36,7 +38,7 @@ impl Cell { /// Yields the value, failing if the cell is empty. pub fn take(&self) -> T { - let this = unsafe { transmute_mut(self) }; + let this = unsafe { cast::transmute_mut(self) }; if this.is_empty() { fail!("attempt to take an empty cell"); } @@ -46,7 +48,7 @@ impl Cell { /// Yields the value if the cell is full, or `None` if it is empty. pub fn take_opt(&self) -> Option { - let this = unsafe { transmute_mut(self) }; + let this = unsafe { cast::transmute_mut(self) }; this.value.take() } @@ -72,3 +74,295 @@ fn test_take_empty() { value_cell.take(); value_cell.take(); } + + +/// A mutable memory location with dynamically checked borrow rules +#[no_freeze] +pub struct RefCell { + priv value: T, + priv borrow: BorrowFlag, + priv nc: NonCopyable +} + +// Values [1, MAX-1] represent the number of `Ref` active +// (will not outgrow its range since `uint` is the size of the address space) +type BorrowFlag = uint; +static UNUSED: BorrowFlag = 0; +static WRITING: BorrowFlag = -1; + +impl RefCell { + /// Create a new `RefCell` containing `value` + pub fn new(value: T) -> RefCell { + RefCell { + value: value, + borrow: UNUSED, + nc: NonCopyable + } + } + + /// Consumes the `RefCell`, returning the wrapped value. + pub fn unwrap(self) -> T { + assert!(self.borrow == UNUSED); + self.value + } + + unsafe fn as_mut<'a>(&'a self) -> &'a mut RefCell { + cast::transmute_mut(self) + } + + /// Attempts to immutably borrow the wrapped value. + /// + /// The borrow lasts until the returned `Ref` exits scope. Multiple + /// immutable borrows can be taken out at the same time. + /// + /// Returns `None` if the value is currently mutably borrowed. + pub fn try_borrow<'a>(&'a self) -> Option> { + match self.borrow { + WRITING => None, + _ => { + unsafe { self.as_mut().borrow += 1; } + Some(Ref { parent: self }) + } + } + } + + /// Immutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `Ref` exits scope. Multiple + /// immutable borrows can be taken out at the same time. + /// + /// # Failure + /// + /// Fails if the value is currently mutably borrowed. + pub fn borrow<'a>(&'a self) -> Ref<'a, T> { + match self.try_borrow() { + Some(ptr) => ptr, + None => fail!("RefCell already mutably borrowed") + } + } + + /// Mutably borrows the wrapped value. + /// + /// The borrow lasts untile the returned `RefMut` exits scope. The value + /// cannot be borrowed while this borrow is active. + /// + /// Returns `None` if the value is currently borrowed. + pub fn try_borrow_mut<'a>(&'a self) -> Option> { + match self.borrow { + UNUSED => unsafe { + let mut_self = self.as_mut(); + mut_self.borrow = WRITING; + Some(RefMut { parent: mut_self }) + }, + _ => None + } + } + + /// Mutably borrows the wrapped value. + /// + /// The borrow lasts untile the returned `RefMut` exits scope. The value + /// cannot be borrowed while this borrow is active. + /// + /// # Failure + /// + /// Fails if the value is currently borrowed. + pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> { + match self.try_borrow_mut() { + Some(ptr) => ptr, + None => fail!("RefCell already borrowed") + } + } + + /// Immutably borrows the wrapped value and applies `blk` to it. + /// + /// # Failure + /// + /// Fails if the value is currently mutably borrowed. + #[inline] + pub fn with(&self, blk: |&T| -> U) -> U { + let ptr = self.borrow(); + blk(ptr.get()) + } + + /// Mutably borrows the wrapped value and applies `blk` to it. + /// + /// # Failure + /// + /// Fails if the value is currently borrowed. + #[inline] + pub fn with_mut(&self, blk: |&mut T| -> U) -> U { + let mut ptr = self.borrow_mut(); + blk(ptr.get()) + } +} + +impl Clone for RefCell { + fn clone(&self) -> RefCell { + let x = self.borrow(); + RefCell::new(x.get().clone()) + } +} + +impl DeepClone for RefCell { + fn deep_clone(&self) -> RefCell { + let x = self.borrow(); + RefCell::new(x.get().deep_clone()) + } +} + +impl Eq for RefCell { + fn eq(&self, other: &RefCell) -> bool { + let a = self.borrow(); + let b = other.borrow(); + a.get() == b.get() + } +} + +/// Wraps a borrowed reference to a value in a `RefCell` box. +pub struct Ref<'box, T> { + priv parent: &'box RefCell +} + +#[unsafe_destructor] +impl<'box, T> Drop for Ref<'box, T> { + fn drop(&mut self) { + assert!(self.parent.borrow != WRITING && self.parent.borrow != UNUSED); + unsafe { self.parent.as_mut().borrow -= 1; } + } +} + +impl<'box, T> Ref<'box, T> { + /// Retrieve an immutable reference to the stored value. + #[inline] + pub fn get<'a>(&'a self) -> &'a T { + &self.parent.value + } +} + +/// Wraps a mutable borrowed reference to a value in a `RefCell` box. +pub struct RefMut<'box, T> { + priv parent: &'box mut RefCell +} + +#[unsafe_destructor] +impl<'box, T> Drop for RefMut<'box, T> { + fn drop(&mut self) { + assert!(self.parent.borrow == WRITING); + self.parent.borrow = UNUSED; + } +} + +impl<'box, T> RefMut<'box, T> { + /// Retrieve a mutable reference to the stored value. + #[inline] + pub fn get<'a>(&'a mut self) -> &'a mut T { + &mut self.parent.value + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn double_imm_borrow() { + let x = RefCell::new(0); + let _b1 = x.borrow(); + x.borrow(); + } + + #[test] + fn no_mut_then_imm_borrow() { + let x = RefCell::new(0); + let _b1 = x.borrow_mut(); + assert!(x.try_borrow().is_none()); + } + + #[test] + fn no_imm_then_borrow_mut() { + let x = RefCell::new(0); + let _b1 = x.borrow(); + assert!(x.try_borrow_mut().is_none()); + } + + #[test] + fn no_double_borrow_mut() { + let x = RefCell::new(0); + let _b1 = x.borrow_mut(); + assert!(x.try_borrow_mut().is_none()); + } + + #[test] + fn imm_release_borrow_mut() { + let x = RefCell::new(0); + { + let _b1 = x.borrow(); + } + x.borrow_mut(); + } + + #[test] + fn mut_release_borrow_mut() { + let x = RefCell::new(0); + { + let _b1 = x.borrow_mut(); + } + x.borrow(); + } + + #[test] + fn double_borrow_single_release_no_borrow_mut() { + let x = RefCell::new(0); + let _b1 = x.borrow(); + { + let _b2 = x.borrow(); + } + assert!(x.try_borrow_mut().is_none()); + } + + #[test] + fn with_ok() { + let x = RefCell::new(0); + assert_eq!(1, x.with(|x| *x+1)); + } + + #[test] + #[should_fail] + fn mut_borrow_with() { + let x = RefCell::new(0); + let _b1 = x.borrow_mut(); + x.with(|x| *x+1); + } + + #[test] + fn borrow_with() { + let x = RefCell::new(0); + let _b1 = x.borrow(); + assert_eq!(1, x.with(|x| *x+1)); + } + + #[test] + fn with_mut_ok() { + let x = RefCell::new(0); + x.with_mut(|x| *x += 1); + let b = x.borrow(); + assert_eq!(1, *b.get()); + } + + #[test] + #[should_fail] + fn borrow_with_mut() { + let x = RefCell::new(0); + let _b = x.borrow(); + x.with_mut(|x| *x += 1); + } + + #[test] + #[should_fail] + fn discard_doesnt_unborrow() { + let x = RefCell::new(0); + let _b = x.borrow(); + let _ = _b; + let _b = x.borrow_mut(); + } +} diff --git a/src/libstd/gc.rs b/src/libstd/gc.rs index 5e2ba808d3115..5fe11d310d467 100644 --- a/src/libstd/gc.rs +++ b/src/libstd/gc.rs @@ -55,26 +55,26 @@ impl DeepClone for Gc { #[cfg(test)] mod tests { use super::*; - use cell::Cell; + use cell::RefCell; #[test] fn test_clone() { - let x = Gc::new(Cell::new(5)); + let x = Gc::new(RefCell::new(5)); let y = x.clone(); - do x.borrow().with_mut_ref |inner| { + do x.borrow().with_mut |inner| { *inner = 20; } - assert_eq!(y.borrow().take(), 20); + assert_eq!(y.borrow().with(|x| *x), 20); } #[test] fn test_deep_clone() { - let x = Gc::new(Cell::new(5)); + let x = Gc::new(RefCell::new(5)); let y = x.deep_clone(); - do x.borrow().with_mut_ref |inner| { + do x.borrow().with_mut |inner| { *inner = 20; } - assert_eq!(y.borrow().take(), 5); + assert_eq!(y.borrow().with(|x| *x), 5); } #[test] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 10887369b159f..ffa91df4e8a86 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -164,7 +164,6 @@ pub mod result; pub mod either; pub mod hashmap; pub mod cell; -pub mod mutable; pub mod trie; diff --git a/src/libstd/mutable.rs b/src/libstd/mutable.rs deleted file mode 100644 index 44efbc149c173..0000000000000 --- a/src/libstd/mutable.rs +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A mutable memory location with dynamically checked borrow rules - -use prelude::*; - -use cast; -use util::NonCopyable; - -/// A mutable memory location with dynamically checked borrow rules -#[no_freeze] -pub struct Mut { - priv value: T, - priv borrow: BorrowFlag, - priv nc: NonCopyable -} - -// Values [1, MAX-1] represent the number of `Ref` active -// (will not outgrow its range since `uint` is the size of the address space) -type BorrowFlag = uint; -static UNUSED: BorrowFlag = 0; -static WRITING: BorrowFlag = -1; - -impl Mut { - /// Create a new `Mut` containing `value` - pub fn new(value: T) -> Mut { - Mut { - value: value, - borrow: UNUSED, - nc: NonCopyable - } - } - - /// Consumes the `Mut`, returning the wrapped value. - pub fn unwrap(self) -> T { - assert!(self.borrow == UNUSED); - self.value - } - - unsafe fn as_mut<'a>(&'a self) -> &'a mut Mut { - cast::transmute_mut(self) - } - - /// Attempts to immutably borrow the wrapped value. - /// - /// The borrow lasts until the returned `Ref` exits scope. Multiple - /// immutable borrows can be taken out at the same time. - /// - /// Returns `None` if the value is currently mutably borrowed. - pub fn try_borrow<'a>(&'a self) -> Option> { - match self.borrow { - WRITING => None, - _ => { - unsafe { self.as_mut().borrow += 1; } - Some(Ref { parent: self }) - } - } - } - - /// Immutably borrows the wrapped value. - /// - /// The borrow lasts until the returned `Ref` exits scope. Multiple - /// immutable borrows can be taken out at the same time. - /// - /// # Failure - /// - /// Fails if the value is currently mutably borrowed. - pub fn borrow<'a>(&'a self) -> Ref<'a, T> { - match self.try_borrow() { - Some(ptr) => ptr, - None => fail!("Mut already mutably borrowed") - } - } - - /// Mutably borrows the wrapped value. - /// - /// The borrow lasts untile the returned `RefMut` exits scope. The value - /// cannot be borrowed while this borrow is active. - /// - /// Returns `None` if the value is currently borrowed. - pub fn try_borrow_mut<'a>(&'a self) -> Option> { - match self.borrow { - UNUSED => unsafe { - let mut_self = self.as_mut(); - mut_self.borrow = WRITING; - Some(RefMut { parent: mut_self }) - }, - _ => None - } - } - - /// Mutably borrows the wrapped value. - /// - /// The borrow lasts untile the returned `RefMut` exits scope. The value - /// cannot be borrowed while this borrow is active. - /// - /// # Failure - /// - /// Fails if the value is currently borrowed. - pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> { - match self.try_borrow_mut() { - Some(ptr) => ptr, - None => fail!("Mut already borrowed") - } - } - - /// Immutably borrows the wrapped value and applies `blk` to it. - /// - /// # Failure - /// - /// Fails if the value is currently mutably borrowed. - #[inline] - pub fn with(&self, blk: |&T| -> U) -> U { - let ptr = self.borrow(); - blk(ptr.get()) - } - - /// Mutably borrows the wrapped value and applies `blk` to it. - /// - /// # Failure - /// - /// Fails if the value is currently borrowed. - #[inline] - pub fn with_mut(&self, blk: |&mut T| -> U) -> U { - let mut ptr = self.borrow_mut(); - blk(ptr.get()) - } -} - -impl Clone for Mut { - fn clone(&self) -> Mut { - let x = self.borrow(); - Mut::new(x.get().clone()) - } -} - -impl DeepClone for Mut { - fn deep_clone(&self) -> Mut { - let x = self.borrow(); - Mut::new(x.get().deep_clone()) - } -} - -impl Eq for Mut { - fn eq(&self, other: &Mut) -> bool { - let a = self.borrow(); - let b = other.borrow(); - a.get() == b.get() - } -} - -/// Wraps a borrowed reference to a value in a `Mut` box. -pub struct Ref<'box, T> { - priv parent: &'box Mut -} - -#[unsafe_destructor] -impl<'box, T> Drop for Ref<'box, T> { - fn drop(&mut self) { - assert!(self.parent.borrow != WRITING && self.parent.borrow != UNUSED); - unsafe { self.parent.as_mut().borrow -= 1; } - } -} - -impl<'box, T> Ref<'box, T> { - /// Retrieve an immutable reference to the stored value. - #[inline] - pub fn get<'a>(&'a self) -> &'a T { - &self.parent.value - } -} - -/// Wraps a mutable borrowed reference to a value in a `Mut` box. -pub struct RefMut<'box, T> { - priv parent: &'box mut Mut -} - -#[unsafe_destructor] -impl<'box, T> Drop for RefMut<'box, T> { - fn drop(&mut self) { - assert!(self.parent.borrow == WRITING); - self.parent.borrow = UNUSED; - } -} - -impl<'box, T> RefMut<'box, T> { - /// Retrieve a mutable reference to the stored value. - #[inline] - pub fn get<'a>(&'a mut self) -> &'a mut T { - &mut self.parent.value - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn double_imm_borrow() { - let x = Mut::new(0); - let _b1 = x.borrow(); - x.borrow(); - } - - #[test] - fn no_mut_then_imm_borrow() { - let x = Mut::new(0); - let _b1 = x.borrow_mut(); - assert!(x.try_borrow().is_none()); - } - - #[test] - fn no_imm_then_borrow_mut() { - let x = Mut::new(0); - let _b1 = x.borrow(); - assert!(x.try_borrow_mut().is_none()); - } - - #[test] - fn no_double_borrow_mut() { - let x = Mut::new(0); - let _b1 = x.borrow_mut(); - assert!(x.try_borrow_mut().is_none()); - } - - #[test] - fn imm_release_borrow_mut() { - let x = Mut::new(0); - { - let _b1 = x.borrow(); - } - x.borrow_mut(); - } - - #[test] - fn mut_release_borrow_mut() { - let x = Mut::new(0); - { - let _b1 = x.borrow_mut(); - } - x.borrow(); - } - - #[test] - fn double_borrow_single_release_no_borrow_mut() { - let x = Mut::new(0); - let _b1 = x.borrow(); - { - let _b2 = x.borrow(); - } - assert!(x.try_borrow_mut().is_none()); - } - - #[test] - fn with_ok() { - let x = Mut::new(0); - assert_eq!(1, x.with(|x| *x+1)); - } - - #[test] - #[should_fail] - fn mut_borrow_with() { - let x = Mut::new(0); - let _b1 = x.borrow_mut(); - x.with(|x| *x+1); - } - - #[test] - fn borrow_with() { - let x = Mut::new(0); - let _b1 = x.borrow(); - assert_eq!(1, x.with(|x| *x+1)); - } - - #[test] - fn with_mut_ok() { - let x = Mut::new(0); - x.with_mut(|x| *x += 1); - let b = x.borrow(); - assert_eq!(1, *b.get()); - } - - #[test] - #[should_fail] - fn borrow_with_mut() { - let x = Mut::new(0); - let _b = x.borrow(); - x.with_mut(|x| *x += 1); - } - - #[test] - #[should_fail] - fn discard_doesnt_unborrow() { - let x = Mut::new(0); - let _b = x.borrow(); - let _ = _b; - let _b = x.borrow_mut(); - } -} diff --git a/src/libstd/rc.rs b/src/libstd/rc.rs index 24e7decfb8235..242533773d7ce 100644 --- a/src/libstd/rc.rs +++ b/src/libstd/rc.rs @@ -21,7 +21,7 @@ use unstable::intrinsics::transmute; use ops::Drop; use kinds::{Freeze, Send}; use clone::{Clone, DeepClone}; -use mutable::Mut; +use cell::RefCell; struct RcBox { value: T, @@ -55,10 +55,10 @@ impl Rc { } } -impl Rc> { - /// Construct a new reference-counted box from a `Mut`-wrapped `Freeze` value +impl Rc> { + /// Construct a new reference-counted box from a `RefCell`-wrapped `Freeze` value #[inline] - pub fn from_mut(value: Mut) -> Rc> { + pub fn from_mut(value: RefCell) -> Rc> { unsafe { Rc::new_unchecked(value) } @@ -116,11 +116,11 @@ impl Drop for Rc { #[cfg(test)] mod test_rc { use super::*; - use mutable::Mut; + use cell::RefCell; #[test] fn test_clone() { - let x = Rc::from_send(Mut::new(5)); + let x = Rc::from_send(RefCell::new(5)); let y = x.clone(); do x.borrow().with_mut |inner| { *inner = 20; @@ -130,7 +130,7 @@ mod test_rc { #[test] fn test_deep_clone() { - let x = Rc::from_send(Mut::new(5)); + let x = Rc::from_send(RefCell::new(5)); let y = x.deep_clone(); do x.borrow().with_mut |inner| { *inner = 20; @@ -161,6 +161,6 @@ mod test_rc { #[test] fn test_from_mut() { let a = 10; - let _x = Rc::from_mut(Mut::new(&a)); + let _x = Rc::from_mut(RefCell::new(&a)); } } diff --git a/src/libstd/rt/comm.rs b/src/libstd/rt/comm.rs index 7441d0d3edcca..8635f9372c472 100644 --- a/src/libstd/rt/comm.rs +++ b/src/libstd/rt/comm.rs @@ -25,10 +25,9 @@ use unstable::sync::UnsafeArc; use util; use util::Void; use comm::{GenericChan, GenericSmartChan, GenericPort, Peekable, SendDeferred}; -use cell::Cell; +use cell::{Cell, RefCell}; use clone::Clone; use tuple::ImmutableTuple; -use mutable::Mut; /// A combined refcount / BlockedTask-as-uint pointer. /// @@ -433,20 +432,20 @@ type StreamPortOne = PortOne>; /// A channel with unbounded size. pub struct Chan { - // FIXME #5372. Using Mut because we don't take &mut self - next: Mut> + // FIXME #5372. Using RefCell because we don't take &mut self + next: RefCell> } /// An port with unbounded size. pub struct Port { - // FIXME #5372. Using Mut because we don't take &mut self - next: Mut>> + // FIXME #5372. Using RefCell because we don't take &mut self + next: RefCell>> } pub fn stream() -> (Port, Chan) { let (pone, cone) = oneshot(); - let port = Port { next: Mut::new(Some(pone)) }; - let chan = Chan { next: Mut::new(cone) }; + let port = Port { next: RefCell::new(Some(pone)) }; + let chan = Chan { next: RefCell::new(cone) }; return (port, chan); } diff --git a/src/test/compile-fail/issue-7013.rs b/src/test/compile-fail/issue-7013.rs index feea0765ec22b..9276a2f0d4194 100644 --- a/src/test/compile-fail/issue-7013.rs +++ b/src/test/compile-fail/issue-7013.rs @@ -9,21 +9,21 @@ // except according to those terms. use std::rc::Rc; -use std::mutable::Mut; +use std::cell::RefCell; trait Foo { - fn set(&mut self, v: Rc>); + fn set(&mut self, v: Rc>); } struct B { - v: Option>> + v: Option>> } impl Foo for B { - fn set(&mut self, v: Rc>) + fn set(&mut self, v: Rc>) { self.v = Some(v); } @@ -37,7 +37,7 @@ struct A fn main() { let a = A {v: ~B{v: None} as ~Foo}; //~ ERROR cannot pack type `~B`, which does not fulfill `Send` - let v = Rc::from_send(Mut::new(a)); + let v = Rc::from_send(RefCell::new(a)); let w = v.clone(); let b = v.borrow(); let mut b = b.borrow_mut(); diff --git a/src/test/compile-fail/mut-cant-alias.rs b/src/test/compile-fail/mut-cant-alias.rs index 8e37f88af8203..f031467328f0b 100644 --- a/src/test/compile-fail/mut-cant-alias.rs +++ b/src/test/compile-fail/mut-cant-alias.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mutable::Mut; +use std::cell::RefCell; fn main() { - let m = Mut::new(0); + let m = RefCell::new(0); let mut b = m.borrow_mut(); let b1 = b.get(); let b2 = b.get(); //~ ERROR cannot borrow `b` as mutable more than once at a time diff --git a/src/test/compile-fail/mut-not-freeze.rs b/src/test/compile-fail/mut-not-freeze.rs index 9a9669cbb3bbd..97fe49ca087f5 100644 --- a/src/test/compile-fail/mut-not-freeze.rs +++ b/src/test/compile-fail/mut-not-freeze.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mutable::Mut; +use std::cell::RefCell; fn f(_: T) {} fn main() { - let x = Mut::new(0); + let x = RefCell::new(0); f(x); //~ ERROR: which does not fulfill `Freeze` } diff --git a/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs b/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs index 0dbba87019aae..ca276700e8b61 100644 --- a/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs +++ b/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mutable::Mut; +use std::cell::RefCell; fn main() { - let m = Mut::new(0); + let m = RefCell::new(0); let p; { let b = m.borrow(); diff --git a/src/test/compile-fail/rcmut-not-const-and-not-owned.rs b/src/test/compile-fail/rcmut-not-const-and-not-owned.rs index 981cec6f0ba19..7e0c8319356e4 100644 --- a/src/test/compile-fail/rcmut-not-const-and-not-owned.rs +++ b/src/test/compile-fail/rcmut-not-const-and-not-owned.rs @@ -8,14 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mutable::Mut; +use std::cell::RefCell; use std::rc::Rc; fn o(_: &T) {} fn c(_: &T) {} fn main() { - let x = Rc::from_send(Mut::new(0)); - o(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::Rc>`, which does not fulfill `Send` - c(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::Rc>`, which does not fulfill `Freeze` + let x = Rc::from_send(RefCell::new(0)); + o(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::Rc>`, which does not fulfill `Send` + c(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::Rc>`, which does not fulfill `Freeze` }