Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Arc/Rc raw pointer conversions for ?Sized #44073

Merged
merged 1 commit into from
Sep 17, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions src/liballoc/arc.rs
Original file line number Diff line number Diff line change
@@ -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
@@ -1491,6 +1498,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);
19 changes: 0 additions & 19 deletions src/liballoc/macros.rs
Original file line number Diff line number Diff line change
@@ -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
}};
}
42 changes: 35 additions & 7 deletions src/liballoc/rc.rs
Original file line number Diff line number Diff line change
@@ -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
@@ -1481,6 +1487,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);