about summary refs log tree commit diff
path: root/src/liballoc
diff options
context:
space:
mode:
authorMurarth <murarth@gmail.com>2017-08-23 22:50:00 -0700
committerMurarth <murarth@gmail.com>2017-09-16 16:34:13 -0700
commit1cbb2b3a8827a2c7d37c022da1e706229b0e23af (patch)
treeb1fc69d8c2ca9c8e6fd7c65d2f94faedfa4d4213 /src/liballoc
parentb965d55a3214b78ec9ffdd3017cb6aaa62c28059 (diff)
downloadrust-1cbb2b3a8827a2c7d37c022da1e706229b0e23af.tar.gz
rust-1cbb2b3a8827a2c7d37c022da1e706229b0e23af.zip
Implement `Arc`/`Rc` raw pointer conversions for `?Sized`
* Add `T: ?Sized` bound to {`Arc`,`Rc`}::{`from_raw`,`into_raw`}
Diffstat (limited to 'src/liballoc')
-rw-r--r--src/liballoc/arc.rs43
-rw-r--r--src/liballoc/macros.rs19
-rw-r--r--src/liballoc/rc.rs42
3 files changed, 71 insertions, 33 deletions
diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs
index d734ae6a2cf..4b695ad7c79 100644
--- a/src/liballoc/arc.rs
+++ b/src/liballoc/arc.rs
@@ -22,7 +22,7 @@ use core::borrow;
 use core::fmt;
 use core::cmp::Ordering;
 use core::intrinsics::abort;
-use core::mem::{self, size_of_val, uninitialized};
+use core::mem::{self, align_of_val, size_of_val, uninitialized};
 use core::ops::Deref;
 use core::ops::CoerceUnsized;
 use core::ptr::{self, Shared};
@@ -324,7 +324,9 @@ impl<T> Arc<T> {
             Ok(elem)
         }
     }
+}
 
+impl<T: ?Sized> Arc<T> {
     /// Consumes the `Arc`, returning the wrapped pointer.
     ///
     /// To avoid a memory leak the pointer must be converted back to an `Arc` using
@@ -378,16 +380,21 @@ impl<T> Arc<T> {
     /// ```
     #[stable(feature = "rc_raw", since = "1.17.0")]
     pub unsafe fn from_raw(ptr: *const T) -> Self {
-        // To find the corresponding pointer to the `ArcInner` we need to subtract the offset of the
-        // `data` field from the pointer.
-        let ptr = (ptr as *const u8).offset(-offset_of!(ArcInner<T>, data));
+        // Align the unsized value to the end of the ArcInner.
+        // Because it is ?Sized, it will always be the last field in memory.
+        let align = align_of_val(&*ptr);
+        let layout = Layout::new::<ArcInner<()>>();
+        let offset = (layout.size() + layout.padding_needed_for(align)) as isize;
+
+        // Reverse the offset to find the original ArcInner.
+        let fake_ptr = ptr as *mut ArcInner<T>;
+        let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset));
+
         Arc {
-            ptr: Shared::new_unchecked(ptr as *mut u8 as *mut _),
+            ptr: Shared::new_unchecked(arc_ptr),
         }
     }
-}
 
-impl<T: ?Sized> Arc<T> {
     /// Creates a new [`Weak`][weak] pointer to this value.
     ///
     /// [weak]: struct.Weak.html
@@ -1492,6 +1499,28 @@ mod tests {
     }
 
     #[test]
+    fn test_into_from_raw_unsized() {
+        use std::fmt::Display;
+        use std::string::ToString;
+
+        let arc: Arc<str> = Arc::from("foo");
+
+        let ptr = Arc::into_raw(arc.clone());
+        let arc2 = unsafe { Arc::from_raw(ptr) };
+
+        assert_eq!(unsafe { &*ptr }, "foo");
+        assert_eq!(arc, arc2);
+
+        let arc: Arc<Display> = Arc::new(123);
+
+        let ptr = Arc::into_raw(arc.clone());
+        let arc2 = unsafe { Arc::from_raw(ptr) };
+
+        assert_eq!(unsafe { &*ptr }.to_string(), "123");
+        assert_eq!(arc2.to_string(), "123");
+    }
+
+    #[test]
     fn test_cowarc_clone_make_mut() {
         let mut cow0 = Arc::new(75);
         let mut cow1 = cow0.clone();
diff --git a/src/liballoc/macros.rs b/src/liballoc/macros.rs
index 43ebaa4fbdb..c2a3019515f 100644
--- a/src/liballoc/macros.rs
+++ b/src/liballoc/macros.rs
@@ -105,22 +105,3 @@ macro_rules! vec {
 macro_rules! format {
     ($($arg:tt)*) => ($crate::fmt::format(format_args!($($arg)*)))
 }
-
-// Private macro to get the offset of a struct field in bytes from the address of the struct.
-macro_rules! offset_of {
-    ($container:path, $field:ident) => {{
-        // Make sure the field actually exists. This line ensures that a compile-time error is
-        // generated if $field is accessed through a Deref impl.
-        let $container { $field : _, .. };
-
-        // Create an (invalid) instance of the container and calculate the offset to its
-        // field. Using a null pointer might be UB if `&(*(0 as *const T)).field` is interpreted to
-        // be nullptr deref.
-        let invalid: $container = ::core::mem::uninitialized();
-        let offset = &invalid.$field as *const _ as usize - &invalid as *const _ as usize;
-
-        // Do not run destructors on the made up invalid instance.
-        ::core::mem::forget(invalid);
-        offset as isize
-    }};
-}
diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs
index 47f537caf31..f11e48d7cf4 100644
--- a/src/liballoc/rc.rs
+++ b/src/liballoc/rc.rs
@@ -252,7 +252,7 @@ use core::hash::{Hash, Hasher};
 use core::intrinsics::abort;
 use core::marker;
 use core::marker::Unsize;
-use core::mem::{self, forget, size_of_val, uninitialized};
+use core::mem::{self, align_of_val, forget, size_of_val, uninitialized};
 use core::ops::Deref;
 use core::ops::CoerceUnsized;
 use core::ptr::{self, Shared};
@@ -358,7 +358,9 @@ impl<T> Rc<T> {
             Err(this)
         }
     }
+}
 
+impl<T: ?Sized> Rc<T> {
     /// Consumes the `Rc`, returning the wrapped pointer.
     ///
     /// To avoid a memory leak the pointer must be converted back to an `Rc` using
@@ -412,17 +414,21 @@ impl<T> Rc<T> {
     /// ```
     #[stable(feature = "rc_raw", since = "1.17.0")]
     pub unsafe fn from_raw(ptr: *const T) -> Self {
-        // To find the corresponding pointer to the `RcBox` we need to subtract the offset of the
-        // `value` field from the pointer.
+        // Align the unsized value to the end of the RcBox.
+        // Because it is ?Sized, it will always be the last field in memory.
+        let align = align_of_val(&*ptr);
+        let layout = Layout::new::<RcBox<()>>();
+        let offset = (layout.size() + layout.padding_needed_for(align)) as isize;
+
+        // Reverse the offset to find the original RcBox.
+        let fake_ptr = ptr as *mut RcBox<T>;
+        let rc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset));
 
-        let ptr = (ptr as *const u8).offset(-offset_of!(RcBox<T>, value));
         Rc {
-            ptr: Shared::new_unchecked(ptr as *mut u8 as *mut _)
+            ptr: Shared::new_unchecked(rc_ptr),
         }
     }
-}
 
-impl<T: ?Sized> Rc<T> {
     /// Creates a new [`Weak`][weak] pointer to this value.
     ///
     /// [weak]: struct.Weak.html
@@ -1482,6 +1488,28 @@ mod tests {
     }
 
     #[test]
+    fn test_into_from_raw_unsized() {
+        use std::fmt::Display;
+        use std::string::ToString;
+
+        let rc: Rc<str> = Rc::from("foo");
+
+        let ptr = Rc::into_raw(rc.clone());
+        let rc2 = unsafe { Rc::from_raw(ptr) };
+
+        assert_eq!(unsafe { &*ptr }, "foo");
+        assert_eq!(rc, rc2);
+
+        let rc: Rc<Display> = Rc::new(123);
+
+        let ptr = Rc::into_raw(rc.clone());
+        let rc2 = unsafe { Rc::from_raw(ptr) };
+
+        assert_eq!(unsafe { &*ptr }.to_string(), "123");
+        assert_eq!(rc2.to_string(), "123");
+    }
+
+    #[test]
     fn get_mut() {
         let mut x = Rc::new(3);
         *Rc::get_mut(&mut x).unwrap() = 4;