diff --git a/src/libextra/extra.rs b/src/libextra/extra.rs index 45e4fe50f2561..1305535dc5030 100644 --- a/src/libextra/extra.rs +++ b/src/libextra/extra.rs @@ -46,7 +46,6 @@ pub use std::os; pub mod c_vec; pub mod io_util; -pub mod rc; // Concurrency diff --git a/src/libextra/rc.rs b/src/libstd/rc.rs similarity index 65% rename from src/libextra/rc.rs rename to src/libstd/rc.rs index fa7cd9025eb7c..50dff380f007e 100644 --- a/src/libextra/rc.rs +++ b/src/libstd/rc.rs @@ -8,31 +8,23 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[allow(missing_doc)]; +/** Task-local reference counted boxes -/** Task-local reference counted smart pointers +The `Rc` type provides shared ownership of an immutable value. Destruction is deterministic, and +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. -Task-local reference counted smart pointers are an alternative to managed boxes with deterministic -destruction. They are restricted to containing types that are either `Send` or `Freeze` (or both) to -prevent cycles. - -Neither `Rc` or `RcMut` is ever `Send` and `RcMut` is never `Freeze`. If `T` is `Freeze`, a -cycle cannot be created with `Rc` because there is no way to modify it after creation. +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 std::cast; -use std::ptr; -use std::unstable::intrinsics; - -// Convert ~T into *mut T without dropping it -#[inline] -unsafe fn owned_to_raw(mut box: ~T) -> *mut T { - let ptr = ptr::to_mut_unsafe_ptr(box); - intrinsics::forget(box); - ptr -} +use ptr::RawPtr; +use unstable::intrinsics::transmute; +use ops::Drop; +use kinds::{Freeze, Send}; +use clone::{Clone, DeepClone}; struct RcBox { value: T, @@ -43,50 +35,38 @@ struct RcBox { #[unsafe_no_drop_flag] #[no_send] pub struct Rc { - priv ptr: *mut RcBox, -} - -impl Rc { - unsafe fn new(value: T) -> Rc { - Rc{ptr: owned_to_raw(~RcBox{value: value, count: 1})} - } -} - -impl Rc { - pub fn from_send(value: T) -> Rc { - unsafe { Rc::new(value) } - } + priv ptr: *mut RcBox } impl Rc { - pub fn from_freeze(value: T) -> Rc { - unsafe { Rc::new(value) } + /// Construct a new reference-counted box from a `Freeze` value + #[inline] + pub fn new(value: T) -> Rc { + unsafe { + Rc::new_unchecked(value) + } } } impl Rc { + /// Unsafety construct a new reference-counted box from any value. + /// + /// If the type is not `Freeze`, the `Rc` box will incorrectly still be considered as a `Freeze` + /// type. It is also possible to create cycles, which will leak, and may interact poorly with + /// managed pointers. #[inline] - pub fn borrow<'r>(&'r self) -> &'r T { - unsafe { cast::copy_lifetime(self, &(*self.ptr).value) } + pub unsafe fn new_unchecked(value: T) -> Rc { + Rc{ptr: transmute(~RcBox{value: value, count: 1})} } -} -#[unsafe_destructor] -impl Drop for Rc { - fn drop(&mut self) { - unsafe { - if self.ptr.is_not_null() { - (*self.ptr).count -= 1; - if (*self.ptr).count == 0 { - let _: ~T = cast::transmute(self.ptr); - } - } - } + /// Borrow the value contained in the reference-counted box + #[inline] + pub fn borrow<'r>(&'r self) -> &'r T { + unsafe { &(*self.ptr).value } } } impl Clone for Rc { - /// Return a shallow copy of the reference counted pointer. #[inline] fn clone(&self) -> Rc { unsafe { @@ -97,47 +77,64 @@ impl Clone for Rc { } impl DeepClone for Rc { - /// Return a deep copy of the reference counted pointer. #[inline] fn deep_clone(&self) -> Rc { - unsafe { Rc::new(self.borrow().deep_clone()) } + unsafe { Rc::new_unchecked(self.borrow().deep_clone()) } + } +} + +#[unsafe_destructor] +impl Drop for Rc { + fn drop(&mut self) { + unsafe { + if self.ptr.is_not_null() { + (*self.ptr).count -= 1; + if (*self.ptr).count == 0 { + let _: ~RcBox = transmute(self.ptr); + } + } + } } } #[cfg(test)] mod test_rc { use super::*; - use std::cell::Cell; + use cell::Cell; #[test] fn test_clone() { - let x = Rc::from_send(Cell::new(5)); - let y = x.clone(); - do x.borrow().with_mut_ref |inner| { - *inner = 20; + unsafe { + let x = Rc::new_unchecked(Cell::new(5)); + let y = x.clone(); + do x.borrow().with_mut_ref |inner| { + *inner = 20; + } + assert_eq!(y.borrow().take(), 20); } - assert_eq!(y.borrow().take(), 20); } #[test] fn test_deep_clone() { - let x = Rc::from_send(Cell::new(5)); - let y = x.deep_clone(); - do x.borrow().with_mut_ref |inner| { - *inner = 20; + unsafe { + let x = Rc::new_unchecked(Cell::new(5)); + let y = x.deep_clone(); + do x.borrow().with_mut_ref |inner| { + *inner = 20; + } + assert_eq!(y.borrow().take(), 5); } - assert_eq!(y.borrow().take(), 5); } #[test] fn test_simple() { - let x = Rc::from_freeze(5); + let x = Rc::new(5); assert_eq!(*x.borrow(), 5); } #[test] fn test_simple_clone() { - let x = Rc::from_freeze(5); + let x = Rc::new(5); let y = x.clone(); assert_eq!(*x.borrow(), 5); assert_eq!(*y.borrow(), 5); @@ -145,8 +142,10 @@ mod test_rc { #[test] fn test_destructor() { - let x = Rc::from_send(~5); - assert_eq!(**x.borrow(), 5); + unsafe { + let x = Rc::new_unchecked(~5); + assert_eq!(**x.borrow(), 5); + } } } @@ -171,21 +170,30 @@ pub struct RcMut { priv ptr: *mut RcMutBox, } -impl RcMut { - unsafe fn new(value: T) -> RcMut { - RcMut{ptr: owned_to_raw(~RcMutBox{value: value, count: 1, borrow: Nothing})} +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(value) } + unsafe { RcMut::new_unchecked(value) } } } -impl RcMut { - pub fn from_freeze(value: T) -> RcMut { - unsafe { RcMut::new(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})} } } @@ -223,7 +231,7 @@ impl Drop for RcMut { if self.ptr.is_not_null() { (*self.ptr).count -= 1; if (*self.ptr).count == 0 { - let _: ~T = cast::transmute(self.ptr); + let _: ~RcMutBox = transmute(self.ptr); } } } @@ -247,7 +255,7 @@ impl DeepClone for RcMut { fn deep_clone(&self) -> RcMut { do self.with_borrow |x| { // FIXME: #6497: should avoid freeze (slow) - unsafe { RcMut::new(x.deep_clone()) } + unsafe { RcMut::new_unchecked(x.deep_clone()) } } } } @@ -270,7 +278,7 @@ mod test_rc_mut { #[test] fn test_deep_clone() { - let x = RcMut::from_freeze(5); + let x = RcMut::new(5); let y = x.deep_clone(); do x.with_mut_borrow |value| { *value = 20; @@ -298,7 +306,7 @@ mod test_rc_mut { #[test] fn modify() { - let x = RcMut::from_freeze(5); + let x = RcMut::new(5); let y = x.clone(); do y.with_mut_borrow |a| { @@ -320,7 +328,7 @@ mod test_rc_mut { #[test] fn release_mutable() { - let x = RcMut::from_freeze(5); + let x = RcMut::new(5); do x.with_mut_borrow |_| {} do x.with_borrow |_| {} } @@ -340,7 +348,7 @@ mod test_rc_mut { #[test] #[should_fail] fn mutable_dupe() { - let x = RcMut::from_freeze(5); + let x = RcMut::new(5); let y = x.clone(); do x.with_mut_borrow |_| { @@ -364,7 +372,7 @@ mod test_rc_mut { #[test] #[should_fail] fn restore_freeze() { - let x = RcMut::from_freeze(5); + let x = RcMut::new(5); let y = x.clone(); do x.with_borrow |_| { diff --git a/src/libstd/std.rs b/src/libstd/std.rs index 5a3f40f280fff..73cb6a5645de3 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -129,6 +129,7 @@ pub mod ptr; pub mod owned; pub mod managed; pub mod borrow; +pub mod rc; /* Core language traits */ diff --git a/src/test/compile-fail/issue-7013.rs b/src/test/compile-fail/issue-7013.rs index fa22665e8c355..5f39aac19af0d 100644 --- a/src/test/compile-fail/issue-7013.rs +++ b/src/test/compile-fail/issue-7013.rs @@ -8,8 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern mod extra; -use extra::rc::RcMut; +use std::rc::RcMut; trait Foo { @@ -37,7 +36,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 = RcMut::from_freeze(a); //~ ERROR instantiating a type parameter with an incompatible type + let v = RcMut::new(a); //~ ERROR instantiating a type parameter with an incompatible type let w = v.clone(); v.with_mut_borrow(|p| {p.v.set(w.clone());}) } diff --git a/src/test/compile-fail/mutable-enum.rs b/src/test/compile-fail/no_freeze-enum.rs similarity index 100% rename from src/test/compile-fail/mutable-enum.rs rename to src/test/compile-fail/no_freeze-enum.rs diff --git a/src/test/compile-fail/mutable-struct.rs b/src/test/compile-fail/no_freeze-struct.rs similarity index 100% rename from src/test/compile-fail/mutable-struct.rs rename to src/test/compile-fail/no_freeze-struct.rs diff --git a/src/test/compile-fail/non_owned-enum.rs b/src/test/compile-fail/no_send-enum.rs similarity index 100% rename from src/test/compile-fail/non_owned-enum.rs rename to src/test/compile-fail/no_send-enum.rs diff --git a/src/test/compile-fail/no_send-rc.rs b/src/test/compile-fail/no_send-rc.rs new file mode 100644 index 0000000000000..d884ff7f23168 --- /dev/null +++ b/src/test/compile-fail/no_send-rc.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::rc::Rc; + +fn bar(_: T) {} + +fn main() { + let x = Rc::new(5); + bar(x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::Rc`, which does not fulfill `Send` +} diff --git a/src/test/compile-fail/non_owned-struct.rs b/src/test/compile-fail/no_send-struct.rs similarity index 100% rename from src/test/compile-fail/non_owned-struct.rs rename to src/test/compile-fail/no_send-struct.rs 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 d645f35d96d7c..fce1f592c6268 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,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern mod extra; +use std::rc::RcMut; fn o(_: &T) {} fn c(_: &T) {} fn main() { - let x = extra::rc::RcMut::from_send(0); - o(&x); //~ ERROR instantiating a type parameter with an incompatible type `extra::rc::RcMut`, which does not fulfill `Send` - c(&x); //~ ERROR instantiating a type parameter with an incompatible type `extra::rc::RcMut`, which does not fulfill `Freeze` + 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` }