Skip to content

Commit f20fbab

Browse files
authored
Rollup merge of rust-lang#44073 - murarth:rc-into-raw-unsized, r=alexcrichton
Implement `Arc`/`Rc` raw pointer conversions for `?Sized` * Add `T: ?Sized` bound to {`Arc`,`Rc`}::{`from_raw`,`into_raw`}
2 parents ef227f5 + 1cbb2b3 commit f20fbab

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
@@ -253,7 +253,7 @@ use core::hash::{Hash, Hasher};
253253
use core::intrinsics::abort;
254254
use core::marker;
255255
use core::marker::Unsize;
256-
use core::mem::{self, forget, size_of_val, uninitialized};
256+
use core::mem::{self, align_of_val, forget, size_of_val, uninitialized};
257257
use core::ops::Deref;
258258
use core::ops::CoerceUnsized;
259259
use core::ptr::{self, Shared};
@@ -359,7 +359,9 @@ impl<T> Rc<T> {
359359
Err(this)
360360
}
361361
}
362+
}
362363

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

419-
let ptr = (ptr as *const u8).offset(-offset_of!(RcBox<T>, value));
420428
Rc {
421-
ptr: Shared::new_unchecked(ptr as *mut u8 as *mut _)
429+
ptr: Shared::new_unchecked(rc_ptr),
422430
}
423431
}
424-
}
425432

426-
impl<T: ?Sized> Rc<T> {
427433
/// Creates a new [`Weak`][weak] pointer to this value.
428434
///
429435
/// [weak]: struct.Weak.html
@@ -1522,6 +1528,28 @@ mod tests {
15221528
}
15231529
}
15241530

1531+
#[test]
1532+
fn test_into_from_raw_unsized() {
1533+
use std::fmt::Display;
1534+
use std::string::ToString;
1535+
1536+
let rc: Rc<str> = Rc::from("foo");
1537+
1538+
let ptr = Rc::into_raw(rc.clone());
1539+
let rc2 = unsafe { Rc::from_raw(ptr) };
1540+
1541+
assert_eq!(unsafe { &*ptr }, "foo");
1542+
assert_eq!(rc, rc2);
1543+
1544+
let rc: Rc<Display> = Rc::new(123);
1545+
1546+
let ptr = Rc::into_raw(rc.clone());
1547+
let rc2 = unsafe { Rc::from_raw(ptr) };
1548+
1549+
assert_eq!(unsafe { &*ptr }.to_string(), "123");
1550+
assert_eq!(rc2.to_string(), "123");
1551+
}
1552+
15251553
#[test]
15261554
fn get_mut() {
15271555
let mut x = Rc::new(3);

0 commit comments

Comments
 (0)