-
Notifications
You must be signed in to change notification settings - Fork 590
Document valid pointer and Box transmutations
#2282
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
base: master
Are you sure you want to change the base?
Changes from all commits
91e9963
e3cc399
3f9733a
e57f861
4f2f844
6ca17b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,33 @@ r[lang-types.box.fundamental] | |
|
|
||
| <!-- Editor Note: This is nowhere close to an exhaustive list --> | ||
|
|
||
| r[lang-types.box.transmute] | ||
| For types `T` and `U` such that `*mut T` and `*mut U` have the same layout (per [layout.pointer.parametric]) and a [pointer-to-pointer cast] from `*mut T` to `*mut U` is permitted, transmuting a `Box<T>` to a `Box<U>`, where both boxes use the global allocator, is sound when both of the following hold: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious, why "global" here?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Box with a custom allocator is not necessarily going to put the allocator in the same place -- e.g. under -Zrandomize-layout -- for different value types, so I don't think we can make this guarantee. Maybe it can be made for 1-ZST allocators? Not sure... |
||
|
|
||
| - The pointee has the same [size] and [alignment] whether viewed as `T` or as `U`. | ||
| - The pointee is a valid value of `U`. | ||
|
|
||
| ```rust | ||
| # use core::mem::transmute; | ||
| let boxed: Box<i8> = Box::new(1); | ||
| let addr = (&raw const *boxed).addr(); | ||
| // SAFETY: `i8` and `u8` have the same size and the same alignment, so | ||
| // `boxed`'s allocation is valid for `u8`. | ||
| let reboxed: Box<u8> = unsafe { transmute(boxed) }; | ||
| // The transmute reuses the same allocation. | ||
| assert_eq!((&raw const *reboxed).addr(), addr); | ||
| assert_eq!(*reboxed, 1); | ||
|
|
||
| // The pointee may also be unsized. | ||
| assert_eq!(size_of::<u32>(), size_of::<f32>()); | ||
| assert_eq!(align_of::<u32>(), align_of::<f32>()); | ||
| let slice: Box<[u32]> = Box::new([1, 2, 3]); | ||
| // SAFETY: Every bit pattern is a valid `f32`, and transmuting the box | ||
| // preserves the slice's length. | ||
| let floats: Box<[f32]> = unsafe { transmute(slice) }; | ||
| assert_eq!(floats.len(), 3); | ||
| ``` | ||
|
|
||
| r[lang-types.rc] | ||
| ## `Rc<T>` | ||
|
|
||
|
|
@@ -186,6 +213,7 @@ These implicit `Sized` bounds may be relaxed by using the special `?Sized` bound | |
| [`UnwindSafe`]: std::panic::UnwindSafe | ||
| [`Unpin`]: std::marker::Unpin | ||
|
|
||
| [alignment]: layout.properties.align | ||
| [Arrays]: types/array.md | ||
| [associated types]: items/associated-items.md#associated-types | ||
| [call expressions]: expressions/call-expr.md | ||
|
|
@@ -205,6 +233,8 @@ These implicit `Sized` bounds may be relaxed by using the special `?Sized` bound | |
| [moved from]: expr.move.movable-place | ||
| [operators]: expressions/operator-expr.md | ||
| [orphan rules]: items/implementations.md#trait-implementation-coherence | ||
| [pointer-to-pointer cast]: expr.as.pointer | ||
| [size]: layout.properties.size | ||
| [`static` items]: items/static-items.md | ||
| [test functions]: attributes/testing.md#the-test-attribute | ||
| [the standard library]: std | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,6 +69,40 @@ Pointers to unsized types are sized. The size and alignment of a pointer to an u | |
| > [!NOTE] | ||
| > Though you should not rely on this, all pointers to <abbr title="Dynamically Sized Types">DSTs</abbr> are currently twice the size of the size of `usize` and have the same alignment. | ||
|
|
||
| r[layout.pointer.parametric] | ||
| The raw pointer types `*const T` and `*const U` have the same layout, as do `*mut T` and `*mut U`, the shared references `&T` and `&U`, and the mutable references `&mut T` and `&mut U`, in each of the following cases: | ||
|
|
||
| - `T` and `U` are both sized types. | ||
| - `T` and `U` both have a [slice] or [`str`] as their [unsized tail]. | ||
| - `T` and `U` both have a [trait object] as their unsized tail. | ||
|
|
||
| ```rust | ||
| # use core::alloc::Layout; | ||
| macro_rules! assert_layout_eq { | ||
| ($a:ty, $b:ty) => { | ||
| assert_eq!(Layout::new::<$a>(), Layout::new::<$b>()) | ||
| }; | ||
| } | ||
| trait Tr1 {} | ||
| trait Tr2 {} | ||
| struct W<T: ?Sized> { | ||
| head: u8, | ||
| tail: T, | ||
| } | ||
| // Pointers to two types share a layout when the types are both sized, | ||
| // both have a slice or `str` tail, or both have a trait object tail. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, I guess the pointers would share a layout even under -Zrandomize-layout? Though that may not guarantee transmutes are sound (since alignment could change). |
||
| assert_layout_eq!(*const u8, *const u64); | ||
| assert_layout_eq!(*const [u8], *const [u64]); | ||
| assert_layout_eq!(*const str, *const [u8]); | ||
| assert_layout_eq!(*const dyn Tr1, *const dyn Tr2); | ||
| assert_layout_eq!(*const W<[u8]>, *const W<[u64]>); | ||
| // References share a layout on the same terms. | ||
| assert_layout_eq!(&u8, &u64); | ||
| # assert_layout_eq!(&str, &[u8]); | ||
| # assert_layout_eq!(&dyn Tr1, &dyn Tr2); | ||
| # assert_layout_eq!(&W<[u8]>, &W<[u64]>); | ||
| ``` | ||
|
|
||
| r[layout.array] | ||
| ## Array layout | ||
|
|
||
|
|
@@ -571,3 +605,7 @@ Because this representation delegates type layout to another type, it cannot be | |
| [structs]: items/structs.md | ||
| [`transparent`]: #the-transparent-representation | ||
| [`Layout`]: std::alloc::Layout | ||
| [slice]: types/slice.md | ||
| [`str`]: types/str.md | ||
| [trait object]: types/trait-object.md | ||
| [unsized tail]: dynamic-sized.tail | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comparing notes with https://doc.rust-lang.org/nightly/std/ptr/trait.Pointee.html#pointer-metadata, I think the only missing piece is the "unsized / last field" indirection for indirectly unsized types.
View changes since the review