From 8e0d01b432b0683d13429dcce907a2b38f30b9f6 Mon Sep 17 00:00:00 2001 From: Murarth Date: Thu, 15 Jun 2017 14:10:43 -0700 Subject: [PATCH] Implement `From<&[T]>` and others for `Arc`/`Rc` Implements RFC 1845, adding implementations of: * `From<&[T]>` for `Rc<[T]>` * `From<&str>` for `Rc` * `From` for `Rc` * `From>` for `Rc` * `From>` for `Rc<[T]>` * and likewise for `Arc<_>` Also removes now-obsolete internal methods `Rc::__from_array` and `Rc::__from_str`, replacing their use with `Rc::from`. --- src/liballoc/arc.rs | 300 +++++++++++++++++++++++++- src/liballoc/rc.rs | 351 +++++++++++++++++++++++++------ src/librustc_metadata/decoder.rs | 2 +- 3 files changed, 587 insertions(+), 66 deletions(-) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index b967eaaaab5b1..d734ae6a2cf61 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -16,16 +16,13 @@ //! //! [arc]: struct.Arc.html -use boxed::Box; - use core::sync::atomic; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; use core::borrow; use core::fmt; use core::cmp::Ordering; use core::intrinsics::abort; -use core::mem; -use core::mem::uninitialized; +use core::mem::{self, size_of_val, uninitialized}; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; @@ -34,7 +31,10 @@ use core::hash::{Hash, Hasher}; use core::{isize, usize}; use core::convert::From; -use heap::{Heap, Alloc, Layout}; +use heap::{Heap, Alloc, Layout, box_free}; +use boxed::Box; +use string::String; +use vec::Vec; /// A soft limit on the amount of references that may be made to an `Arc`. /// @@ -532,6 +532,141 @@ impl Arc { } } +impl Arc { + // Allocates an `ArcInner` with sufficient space for an unsized value + unsafe fn allocate_for_ptr(ptr: *const T) -> *mut ArcInner { + // Create a fake ArcInner to find allocation size and alignment + let fake_ptr = ptr as *mut ArcInner; + + let layout = Layout::for_value(&*fake_ptr); + + let mem = Heap.alloc(layout) + .unwrap_or_else(|e| Heap.oom(e)); + + // Initialize the real ArcInner + let inner = set_data_ptr(ptr as *mut T, mem) as *mut ArcInner; + + ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1)); + ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); + + inner + } + + fn from_box(v: Box) -> Arc { + unsafe { + let bptr = Box::into_raw(v); + + let value_size = size_of_val(&*bptr); + let ptr = Self::allocate_for_ptr(bptr); + + // Copy value as bytes + ptr::copy_nonoverlapping( + bptr as *const T as *const u8, + &mut (*ptr).data as *mut _ as *mut u8, + value_size); + + // Free the allocation without dropping its contents + box_free(bptr); + + Arc { ptr: Shared::new_unchecked(ptr) } + } + } +} + +// Sets the data pointer of a `?Sized` raw pointer. +// +// For a slice/trait object, this sets the `data` field and leaves the rest +// unchanged. For a sized raw pointer, this simply sets the pointer. +unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { + ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); + ptr +} + +impl Arc<[T]> { + // Copy elements from slice into newly allocated Arc<[T]> + // + // Unsafe because the caller must either take ownership or bind `T: Copy` + unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { + let v_ptr = v as *const [T]; + let ptr = Self::allocate_for_ptr(v_ptr); + + ptr::copy_nonoverlapping( + v.as_ptr(), + &mut (*ptr).data as *mut [T] as *mut T, + v.len()); + + Arc { ptr: Shared::new_unchecked(ptr) } + } +} + +// Specialization trait used for From<&[T]> +trait ArcFromSlice { + fn from_slice(slice: &[T]) -> Self; +} + +impl ArcFromSlice for Arc<[T]> { + #[inline] + default fn from_slice(v: &[T]) -> Self { + // Panic guard while cloning T elements. + // In the event of a panic, elements that have been written + // into the new ArcInner will be dropped, then the memory freed. + struct Guard { + mem: *mut u8, + elems: *mut T, + layout: Layout, + n_elems: usize, + } + + impl Drop for Guard { + fn drop(&mut self) { + use core::slice::from_raw_parts_mut; + + unsafe { + let slice = from_raw_parts_mut(self.elems, self.n_elems); + ptr::drop_in_place(slice); + + Heap.dealloc(self.mem, self.layout.clone()); + } + } + } + + unsafe { + let v_ptr = v as *const [T]; + let ptr = Self::allocate_for_ptr(v_ptr); + + let mem = ptr as *mut _ as *mut u8; + let layout = Layout::for_value(&*ptr); + + // Pointer to first element + let elems = &mut (*ptr).data as *mut [T] as *mut T; + + let mut guard = Guard{ + mem: mem, + elems: elems, + layout: layout, + n_elems: 0, + }; + + for (i, item) in v.iter().enumerate() { + ptr::write(elems.offset(i as isize), item.clone()); + guard.n_elems += 1; + } + + // All clear. Forget the guard so it doesn't free the new ArcInner. + mem::forget(guard); + + Arc { ptr: Shared::new_unchecked(ptr) } + } + } +} + +impl ArcFromSlice for Arc<[T]> { + #[inline] + fn from_slice(v: &[T]) -> Self { + unsafe { Arc::copy_from_slice(v) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Arc { /// Makes a clone of the `Arc` pointer. @@ -1216,8 +1351,56 @@ impl From for Arc { } } +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl<'a, T: Clone> From<&'a [T]> for Arc<[T]> { + #[inline] + fn from(v: &[T]) -> Arc<[T]> { + >::from_slice(v) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl<'a> From<&'a str> for Arc { + #[inline] + fn from(v: &str) -> Arc { + unsafe { mem::transmute(>::from(v.as_bytes())) } + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From for Arc { + #[inline] + fn from(v: String) -> Arc { + Arc::from(&v[..]) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From> for Arc { + #[inline] + fn from(v: Box) -> Arc { + Arc::from_box(v) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From> for Arc<[T]> { + #[inline] + fn from(mut v: Vec) -> Arc<[T]> { + unsafe { + let arc = Arc::copy_from_slice(&v); + + // Allow the Vec to free its memory, but not destroy its contents + v.set_len(0); + + arc + } + } +} + #[cfg(test)] mod tests { + use std::boxed::Box; use std::clone::Clone; use std::sync::mpsc::channel; use std::mem::drop; @@ -1520,6 +1703,113 @@ mod tests { } t.join().unwrap(); } + + #[test] + fn test_from_str() { + let r: Arc = Arc::from("foo"); + + assert_eq!(&r[..], "foo"); + } + + #[test] + fn test_copy_from_slice() { + let s: &[u32] = &[1, 2, 3]; + let r: Arc<[u32]> = Arc::from(s); + + assert_eq!(&r[..], [1, 2, 3]); + } + + #[test] + fn test_clone_from_slice() { + #[derive(Clone, Debug, Eq, PartialEq)] + struct X(u32); + + let s: &[X] = &[X(1), X(2), X(3)]; + let r: Arc<[X]> = Arc::from(s); + + assert_eq!(&r[..], s); + } + + #[test] + #[should_panic] + fn test_clone_from_slice_panic() { + use std::string::{String, ToString}; + + struct Fail(u32, String); + + impl Clone for Fail { + fn clone(&self) -> Fail { + if self.0 == 2 { + panic!(); + } + Fail(self.0, self.1.clone()) + } + } + + let s: &[Fail] = &[ + Fail(0, "foo".to_string()), + Fail(1, "bar".to_string()), + Fail(2, "baz".to_string()), + ]; + + // Should panic, but not cause memory corruption + let _r: Arc<[Fail]> = Arc::from(s); + } + + #[test] + fn test_from_box() { + let b: Box = box 123; + let r: Arc = Arc::from(b); + + assert_eq!(*r, 123); + } + + #[test] + fn test_from_box_str() { + use std::string::String; + + let s = String::from("foo").into_boxed_str(); + let r: Arc = Arc::from(s); + + assert_eq!(&r[..], "foo"); + } + + #[test] + fn test_from_box_slice() { + let s = vec![1, 2, 3].into_boxed_slice(); + let r: Arc<[u32]> = Arc::from(s); + + assert_eq!(&r[..], [1, 2, 3]); + } + + #[test] + fn test_from_box_trait() { + use std::fmt::Display; + use std::string::ToString; + + let b: Box = box 123; + let r: Arc = Arc::from(b); + + assert_eq!(r.to_string(), "123"); + } + + #[test] + fn test_from_box_trait_zero_sized() { + use std::fmt::Debug; + + let b: Box = box (); + let r: Arc = Arc::from(b); + + assert_eq!(format!("{:?}", r), "()"); + } + + #[test] + fn test_from_vec() { + let v = vec![1, 2, 3]; + let r: Arc<[u32]> = Arc::from(v); + + assert_eq!(&r[..], [1, 2, 3]); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 9783aed895f7d..47f537caf31c4 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -252,14 +252,15 @@ use core::hash::{Hash, Hasher}; use core::intrinsics::abort; use core::marker; use core::marker::Unsize; -use core::mem::{self, forget, size_of, size_of_val, uninitialized}; +use core::mem::{self, forget, size_of_val, uninitialized}; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; use core::convert::From; use heap::{Heap, Alloc, Layout, box_free}; -use raw_vec::RawVec; +use string::String; +use vec::Vec; struct RcBox { strong: Cell, @@ -421,64 +422,6 @@ impl Rc { } } -impl Rc { - /// Constructs a new `Rc` from a string slice. - #[doc(hidden)] - #[unstable(feature = "rustc_private", - reason = "for internal use in rustc", - issue = "27812")] - pub fn __from_str(value: &str) -> Rc { - unsafe { - // Allocate enough space for `RcBox`. - let aligned_len = 2 + (value.len() + size_of::() - 1) / size_of::(); - let vec = RawVec::::with_capacity(aligned_len); - let ptr = vec.ptr(); - forget(vec); - // Initialize fields of `RcBox`. - *ptr.offset(0) = 1; // strong: Cell::new(1) - *ptr.offset(1) = 1; // weak: Cell::new(1) - ptr::copy_nonoverlapping(value.as_ptr(), ptr.offset(2) as *mut u8, value.len()); - // Combine the allocation address and the string length into a fat pointer to `RcBox`. - let rcbox_ptr: *mut RcBox = mem::transmute([ptr as usize, value.len()]); - assert!(aligned_len * size_of::() == size_of_val(&*rcbox_ptr)); - Rc { ptr: Shared::new_unchecked(rcbox_ptr) } - } - } -} - -impl Rc<[T]> { - /// Constructs a new `Rc<[T]>` from a `Box<[T]>`. - #[doc(hidden)] - #[unstable(feature = "rustc_private", - reason = "for internal use in rustc", - issue = "27812")] - pub fn __from_array(value: Box<[T]>) -> Rc<[T]> { - unsafe { - let ptr: *mut RcBox<[T]> = - mem::transmute([mem::align_of::>(), value.len()]); - // FIXME(custom-DST): creating this invalid &[T] is dubiously defined, - // we should have a better way of getting the size/align - // of a DST from its unsized part. - let ptr = Heap.alloc(Layout::for_value(&*ptr)) - .unwrap_or_else(|e| Heap.oom(e)); - let ptr: *mut RcBox<[T]> = mem::transmute([ptr as usize, value.len()]); - - // Initialize the new RcBox. - ptr::write(&mut (*ptr).strong, Cell::new(1)); - ptr::write(&mut (*ptr).weak, Cell::new(1)); - ptr::copy_nonoverlapping( - value.as_ptr(), - &mut (*ptr).value as *mut [T] as *mut T, - value.len()); - - // Free the original allocation without freeing its (moved) contents. - box_free(Box::into_raw(value)); - - Rc { ptr: Shared::new_unchecked(ptr as *mut _) } - } - } -} - impl Rc { /// Creates a new [`Weak`][weak] pointer to this value. /// @@ -665,6 +608,140 @@ impl Rc { } } +impl Rc { + // Allocates an `RcBox` with sufficient space for an unsized value + unsafe fn allocate_for_ptr(ptr: *const T) -> *mut RcBox { + // Create a fake RcBox to find allocation size and alignment + let fake_ptr = ptr as *mut RcBox; + + let layout = Layout::for_value(&*fake_ptr); + + let mem = Heap.alloc(layout) + .unwrap_or_else(|e| Heap.oom(e)); + + // Initialize the real RcBox + let inner = set_data_ptr(ptr as *mut T, mem) as *mut RcBox; + + ptr::write(&mut (*inner).strong, Cell::new(1)); + ptr::write(&mut (*inner).weak, Cell::new(1)); + + inner + } + + fn from_box(v: Box) -> Rc { + unsafe { + let bptr = Box::into_raw(v); + + let value_size = size_of_val(&*bptr); + let ptr = Self::allocate_for_ptr(bptr); + + // Copy value as bytes + ptr::copy_nonoverlapping( + bptr as *const T as *const u8, + &mut (*ptr).value as *mut _ as *mut u8, + value_size); + + // Free the allocation without dropping its contents + box_free(bptr); + + Rc { ptr: Shared::new_unchecked(ptr) } + } + } +} + +// Sets the data pointer of a `?Sized` raw pointer. +// +// For a slice/trait object, this sets the `data` field and leaves the rest +// unchanged. For a sized raw pointer, this simply sets the pointer. +unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { + ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); + ptr +} + +impl Rc<[T]> { + // Copy elements from slice into newly allocated Rc<[T]> + // + // Unsafe because the caller must either take ownership or bind `T: Copy` + unsafe fn copy_from_slice(v: &[T]) -> Rc<[T]> { + let v_ptr = v as *const [T]; + let ptr = Self::allocate_for_ptr(v_ptr); + + ptr::copy_nonoverlapping( + v.as_ptr(), + &mut (*ptr).value as *mut [T] as *mut T, + v.len()); + + Rc { ptr: Shared::new_unchecked(ptr) } + } +} + +trait RcFromSlice { + fn from_slice(slice: &[T]) -> Self; +} + +impl RcFromSlice for Rc<[T]> { + #[inline] + default fn from_slice(v: &[T]) -> Self { + // Panic guard while cloning T elements. + // In the event of a panic, elements that have been written + // into the new RcBox will be dropped, then the memory freed. + struct Guard { + mem: *mut u8, + elems: *mut T, + layout: Layout, + n_elems: usize, + } + + impl Drop for Guard { + fn drop(&mut self) { + use core::slice::from_raw_parts_mut; + + unsafe { + let slice = from_raw_parts_mut(self.elems, self.n_elems); + ptr::drop_in_place(slice); + + Heap.dealloc(self.mem, self.layout.clone()); + } + } + } + + unsafe { + let v_ptr = v as *const [T]; + let ptr = Self::allocate_for_ptr(v_ptr); + + let mem = ptr as *mut _ as *mut u8; + let layout = Layout::for_value(&*ptr); + + // Pointer to first element + let elems = &mut (*ptr).value as *mut [T] as *mut T; + + let mut guard = Guard{ + mem: mem, + elems: elems, + layout: layout, + n_elems: 0, + }; + + for (i, item) in v.iter().enumerate() { + ptr::write(elems.offset(i as isize), item.clone()); + guard.n_elems += 1; + } + + // All clear. Forget the guard so it doesn't free the new RcBox. + forget(guard); + + Rc { ptr: Shared::new_unchecked(ptr) } + } + } +} + +impl RcFromSlice for Rc<[T]> { + #[inline] + fn from_slice(v: &[T]) -> Self { + unsafe { Rc::copy_from_slice(v) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Deref for Rc { type Target = T; @@ -959,6 +1036,53 @@ impl From for Rc { } } +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl<'a, T: Clone> From<&'a [T]> for Rc<[T]> { + #[inline] + fn from(v: &[T]) -> Rc<[T]> { + >::from_slice(v) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl<'a> From<&'a str> for Rc { + #[inline] + fn from(v: &str) -> Rc { + unsafe { mem::transmute(>::from(v.as_bytes())) } + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From for Rc { + #[inline] + fn from(v: String) -> Rc { + Rc::from(&v[..]) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From> for Rc { + #[inline] + fn from(v: Box) -> Rc { + Rc::from_box(v) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From> for Rc<[T]> { + #[inline] + fn from(mut v: Vec) -> Rc<[T]> { + unsafe { + let rc = Rc::copy_from_slice(&v); + + // Allow the Vec to free its memory, but not destroy its contents + v.set_len(0); + + rc + } + } +} + /// `Weak` is a version of [`Rc`] that holds a non-owning reference to the /// managed value. The value is accessed by calling [`upgrade`] on the `Weak` /// pointer, which returns an [`Option`]`<`[`Rc`]`>`. @@ -1465,6 +1589,113 @@ mod tests { assert!(Rc::ptr_eq(&five, &same_five)); assert!(!Rc::ptr_eq(&five, &other_five)); } + + #[test] + fn test_from_str() { + let r: Rc = Rc::from("foo"); + + assert_eq!(&r[..], "foo"); + } + + #[test] + fn test_copy_from_slice() { + let s: &[u32] = &[1, 2, 3]; + let r: Rc<[u32]> = Rc::from(s); + + assert_eq!(&r[..], [1, 2, 3]); + } + + #[test] + fn test_clone_from_slice() { + #[derive(Clone, Debug, Eq, PartialEq)] + struct X(u32); + + let s: &[X] = &[X(1), X(2), X(3)]; + let r: Rc<[X]> = Rc::from(s); + + assert_eq!(&r[..], s); + } + + #[test] + #[should_panic] + fn test_clone_from_slice_panic() { + use std::string::{String, ToString}; + + struct Fail(u32, String); + + impl Clone for Fail { + fn clone(&self) -> Fail { + if self.0 == 2 { + panic!(); + } + Fail(self.0, self.1.clone()) + } + } + + let s: &[Fail] = &[ + Fail(0, "foo".to_string()), + Fail(1, "bar".to_string()), + Fail(2, "baz".to_string()), + ]; + + // Should panic, but not cause memory corruption + let _r: Rc<[Fail]> = Rc::from(s); + } + + #[test] + fn test_from_box() { + let b: Box = box 123; + let r: Rc = Rc::from(b); + + assert_eq!(*r, 123); + } + + #[test] + fn test_from_box_str() { + use std::string::String; + + let s = String::from("foo").into_boxed_str(); + let r: Rc = Rc::from(s); + + assert_eq!(&r[..], "foo"); + } + + #[test] + fn test_from_box_slice() { + let s = vec![1, 2, 3].into_boxed_slice(); + let r: Rc<[u32]> = Rc::from(s); + + assert_eq!(&r[..], [1, 2, 3]); + } + + #[test] + fn test_from_box_trait() { + use std::fmt::Display; + use std::string::ToString; + + let b: Box = box 123; + let r: Rc = Rc::from(b); + + assert_eq!(r.to_string(), "123"); + } + + #[test] + fn test_from_box_trait_zero_sized() { + use std::fmt::Debug; + + let b: Box = box (); + let r: Rc = Rc::from(b); + + assert_eq!(format!("{:?}", r), "()"); + } + + #[test] + fn test_from_vec() { + let v = vec![1, 2, 3]; + let r: Rc<[u32]> = Rc::from(v); + + assert_eq!(&r[..], [1, 2, 3]); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 5c37333fc7e82..73b2e972b93ca 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -892,7 +892,7 @@ impl<'a, 'tcx> CrateMetadata { if def_key.disambiguated_data.data == DefPathData::StructCtor { item = self.entry(def_key.parent.unwrap()); } - let result = Rc::__from_array(self.get_attributes(&item).into_boxed_slice()); + let result: Rc<[ast::Attribute]> = Rc::from(self.get_attributes(&item)); let vec_ = &mut self.attribute_cache.borrow_mut()[node_as]; if vec_.len() < node_index + 1 { vec_.resize(node_index + 1, None);