Skip to content

Commit 1cbb2b3

Browse files
committed
Implement Arc/Rc raw pointer conversions for ?Sized
* Add `T: ?Sized` bound to {`Arc`,`Rc`}::{`from_raw`,`into_raw`}
1 parent b965d55 commit 1cbb2b3

File tree

3 files changed

+71
-33
lines changed

3 files changed

+71
-33
lines changed

src/liballoc/arc.rs

+36-7
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use core::borrow;
2222
use core::fmt;
2323
use core::cmp::Ordering;
2424
use core::intrinsics::abort;
25-
use core::mem::{self, size_of_val, uninitialized};
25+
use core::mem::{self, align_of_val, size_of_val, uninitialized};
2626
use core::ops::Deref;
2727
use core::ops::CoerceUnsized;
2828
use core::ptr::{self, Shared};
@@ -324,7 +324,9 @@ impl<T> Arc<T> {
324324
Ok(elem)
325325
}
326326
}
327+
}
327328

329+
impl<T: ?Sized> Arc<T> {
328330
/// Consumes the `Arc`, returning the wrapped pointer.
329331
///
330332
/// To avoid a memory leak the pointer must be converted back to an `Arc` using
@@ -378,16 +380,21 @@ impl<T> Arc<T> {
378380
/// ```
379381
#[stable(feature = "rc_raw", since = "1.17.0")]
380382
pub unsafe fn from_raw(ptr: *const T) -> Self {
381-
// To find the corresponding pointer to the `ArcInner` we need to subtract the offset of the
382-
// `data` field from the pointer.
383-
let ptr = (ptr as *const u8).offset(-offset_of!(ArcInner<T>, data));
383+
// Align the unsized value to the end of the ArcInner.
384+
// Because it is ?Sized, it will always be the last field in memory.
385+
let align = align_of_val(&*ptr);
386+
let layout = Layout::new::<ArcInner<()>>();
387+
let offset = (layout.size() + layout.padding_needed_for(align)) as isize;
388+
389+
// Reverse the offset to find the original ArcInner.
390+
let fake_ptr = ptr as *mut ArcInner<T>;
391+
let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset));
392+
384393
Arc {
385-
ptr: Shared::new_unchecked(ptr as *mut u8 as *mut _),
394+
ptr: Shared::new_unchecked(arc_ptr),
386395
}
387396
}
388-
}
389397

390-
impl<T: ?Sized> Arc<T> {
391398
/// Creates a new [`Weak`][weak] pointer to this value.
392399
///
393400
/// [weak]: struct.Weak.html
@@ -1491,6 +1498,28 @@ mod tests {
14911498
}
14921499
}
14931500

1501+
#[test]
1502+
fn test_into_from_raw_unsized() {
1503+
use std::fmt::Display;
1504+
use std::string::ToString;
1505+
1506+
let arc: Arc<str> = Arc::from("foo");
1507+
1508+
let ptr = Arc::into_raw(arc.clone());
1509+
let arc2 = unsafe { Arc::from_raw(ptr) };
1510+
1511+
assert_eq!(unsafe { &*ptr }, "foo");
1512+
assert_eq!(arc, arc2);
1513+
1514+
let arc: Arc<Display> = Arc::new(123);
1515+
1516+
let ptr = Arc::into_raw(arc.clone());
1517+
let arc2 = unsafe { Arc::from_raw(ptr) };
1518+
1519+
assert_eq!(unsafe { &*ptr }.to_string(), "123");
1520+
assert_eq!(arc2.to_string(), "123");
1521+
}
1522+
14941523
#[test]
14951524
fn test_cowarc_clone_make_mut() {
14961525
let mut cow0 = Arc::new(75);

src/liballoc/macros.rs

-19
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,3 @@ macro_rules! vec {
105105
macro_rules! format {
106106
($($arg:tt)*) => ($crate::fmt::format(format_args!($($arg)*)))
107107
}
108-
109-
// Private macro to get the offset of a struct field in bytes from the address of the struct.
110-
macro_rules! offset_of {
111-
($container:path, $field:ident) => {{
112-
// Make sure the field actually exists. This line ensures that a compile-time error is
113-
// generated if $field is accessed through a Deref impl.
114-
let $container { $field : _, .. };
115-
116-
// Create an (invalid) instance of the container and calculate the offset to its
117-
// field. Using a null pointer might be UB if `&(*(0 as *const T)).field` is interpreted to
118-
// be nullptr deref.
119-
let invalid: $container = ::core::mem::uninitialized();
120-
let offset = &invalid.$field as *const _ as usize - &invalid as *const _ as usize;
121-
122-
// Do not run destructors on the made up invalid instance.
123-
::core::mem::forget(invalid);
124-
offset as isize
125-
}};
126-
}

src/liballoc/rc.rs

+35-7
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ use core::hash::{Hash, Hasher};
252252
use core::intrinsics::abort;
253253
use core::marker;
254254
use core::marker::Unsize;
255-
use core::mem::{self, forget, size_of_val, uninitialized};
255+
use core::mem::{self, align_of_val, forget, size_of_val, uninitialized};
256256
use core::ops::Deref;
257257
use core::ops::CoerceUnsized;
258258
use core::ptr::{self, Shared};
@@ -358,7 +358,9 @@ impl<T> Rc<T> {
358358
Err(this)
359359
}
360360
}
361+
}
361362

363+
impl<T: ?Sized> Rc<T> {
362364
/// Consumes the `Rc`, returning the wrapped pointer.
363365
///
364366
/// To avoid a memory leak the pointer must be converted back to an `Rc` using
@@ -412,17 +414,21 @@ impl<T> Rc<T> {
412414
/// ```
413415
#[stable(feature = "rc_raw", since = "1.17.0")]
414416
pub unsafe fn from_raw(ptr: *const T) -> Self {
415-
// To find the corresponding pointer to the `RcBox` we need to subtract the offset of the
416-
// `value` field from the pointer.
417+
// Align the unsized value to the end of the RcBox.
418+
// Because it is ?Sized, it will always be the last field in memory.
419+
let align = align_of_val(&*ptr);
420+
let layout = Layout::new::<RcBox<()>>();
421+
let offset = (layout.size() + layout.padding_needed_for(align)) as isize;
422+
423+
// Reverse the offset to find the original RcBox.
424+
let fake_ptr = ptr as *mut RcBox<T>;
425+
let rc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset));
417426

418-
let ptr = (ptr as *const u8).offset(-offset_of!(RcBox<T>, value));
419427
Rc {
420-
ptr: Shared::new_unchecked(ptr as *mut u8 as *mut _)
428+
ptr: Shared::new_unchecked(rc_ptr),
421429
}
422430
}
423-
}
424431

425-
impl<T: ?Sized> Rc<T> {
426432
/// Creates a new [`Weak`][weak] pointer to this value.
427433
///
428434
/// [weak]: struct.Weak.html
@@ -1481,6 +1487,28 @@ mod tests {
14811487
}
14821488
}
14831489

1490+
#[test]
1491+
fn test_into_from_raw_unsized() {
1492+
use std::fmt::Display;
1493+
use std::string::ToString;
1494+
1495+
let rc: Rc<str> = Rc::from("foo");
1496+
1497+
let ptr = Rc::into_raw(rc.clone());
1498+
let rc2 = unsafe { Rc::from_raw(ptr) };
1499+
1500+
assert_eq!(unsafe { &*ptr }, "foo");
1501+
assert_eq!(rc, rc2);
1502+
1503+
let rc: Rc<Display> = Rc::new(123);
1504+
1505+
let ptr = Rc::into_raw(rc.clone());
1506+
let rc2 = unsafe { Rc::from_raw(ptr) };
1507+
1508+
assert_eq!(unsafe { &*ptr }.to_string(), "123");
1509+
assert_eq!(rc2.to_string(), "123");
1510+
}
1511+
14841512
#[test]
14851513
fn get_mut() {
14861514
let mut x = Rc::new(3);

0 commit comments

Comments
 (0)