You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#81513 introduces a ptr::Pointee type. This interface lacks a couple key bits of info, two key bits critical for allocators and stuff like ThinBox.
Every pointee in practice has to have some sort of defined layout, and as pointers exist in a finite space, layout is also guaranteed to be finite. rust-lang/rfcs#3536 offers a partial solution for sizes, but doesn't address the two cases where alignment isn't statically known: the dynamic alignment of dyn Trait and the undefined alignment of opaque types.
So, ptr::Pointee needs to expose a way to get this information, as it was initially intended to. There's a few constraints:
It must be type-safe as much as possible. And what can't be checked at the type level must be easily checked in full by Miri.
Where things can be inferred or generated, it must be inferred or generated. Otherwise, it'd disrupt a lot of code.
The simplest way I can think of is unfortunately still somewhat involved, and I'm not sure I can reduce it any further:
Add a required fn layout(&self) -> alloc::Layout method to ptr::Pointee.
Using Layout and &self simplifies the interface a lot.
Add a marker::Aligned trait similar to marker::Sized, representing that the type has a statically defined alignment. Almost everything auto-implements this, but dyn Trait and opaque types will not.
Sized will be modified to subtype this. The only way to declare an unknown alignment is to use an extern type, a type of DST, and so it's not possible to both make the size statically determinable and leave the alignment unknown.
Like its counterpart Sized, Aligned must be implemented to read something by value, and generic parameters implicitly add an Aligned constraint unless you explicitly add ?Aligned. Unlike it, Aligned may be manually implemented for extern types via a #[repr(align(N))] attribute.
Why an attribute? Makes things way easier for the compiler.
Make Pointee an unsafe trait. The lack of safety is because it's defining the memory safety boundaries.
The layout method is safe to call, but the method itself must ensure that:
The returned layout's size holds a full T.
The returned layout's align returns the actual runtime alignment of the &self pointer.
Auto-implement Pointee for every type that either implements Aligned or has a final field that implements Pointee.
If the final field does not implement Aligned but does implement Pointee, the auto-generated implementation returns essentially static_prefix.extend(final_field.layout()).unwrap().0.
Pointee is not auto-implemented extern types. Their layout is undefined, and only the programmer will know how it should be laid out. It also makes for a nice escape hatch.
Generics will assume Pointee to be implemented by default. It'll be like Sized rather than Unpin in that regard.
This avoids breaking things like struct Foo<T>(Arc<T>).
Here's how it'd look for the bulk of this at the type level:
use core::alloc::Layout;use core::ptr::metadata;// `core::marker::Aligned`#[lang = "aligned"]pubtraitAligned{// Empty}// `core::marker::Sized`#[lang = "sized"]pubtraitSized:Aligned{// Empty}// `core::ptr::Pointee`#[lang = "pointee"]pubunsafetraitPointee{typeMetadata:Copy + Send + Sync + Ord + core::hash::Hash + Unpin;fnlayout(&self) -> Layout;}unsafeimpl<T:Sized + Aligned>PointeeforT{typeMetadata = ();constfnlayout(&self) -> Layout{Layout::new::<T>()}}// Generated for every `trait Trait`unsafeimplPointeefordynTrait{typeMetadata = DynMetadata<Self>;fnlayout(&self) -> Layout{metadata(self).layout()}}unsafeimpl<T:Sized + Aligned>Pointeefor[T]{typeMetadata = usize;constfnlayout(&self) -> Layout{let size = core::mem::size_of::<T>();let align = core::mem::align_of::<T>();let len = self.len();unsafe{Layout::from_size_align_unchecked(len.unchecked_mul(size), align)}}}unsafeimplPointeeforstr{typeMetadata = usize;constfnlayout(&self) -> Layout{let len = self.len();unsafe{Layout::from_size_align_unchecked(len,1)}}}// `core::ffi::CStr`extern"C"{#[repr(align(1))]pubtypeCStr;}unsafeimplPointeeforCStr{typeMetadata = ();fnlayout(&self) -> Layout{let len = self.len();unsafe{Layout::from_size_align_unchecked(len.unchecked_add(1),1)}}}// `Layout::for_value` should just delegate to `Pointee::layout`implLayout{fnfor_value<T: ?Sized>(value:&T) -> Layout{
<Tas core::ptr::Pointee>::layout(value)}}// `mem::size_of_val` and `mem::align_of_val` no longer need intrinsicspubfnsize_of_val<T: ?Sized + ?Aligned>(value:&T) -> usize{
<Tas core::ptr::Pointee>::layout(value).size()}pubfnalign_of_val<T: ?Sized + ?Aligned>(value:&T) -> usize{
<Tas core::ptr::Pointee>::layout(value).align()}
There's a couple other benefits this provides:
It removes the need for two compiler intrinsics: size_of_val and align_of_val. Their implementations are of course in the above code block.
Miri can check custom layouts against the struct's backing memory on construct and on every pointer cast to the type, ensuring the referenced memory region is always valid and that the pointer in question is correctly aligned.
#81513 introduces a
ptr::Pointeetype. This interface lacks a couple key bits of info, two key bits critical for allocators and stuff likeThinBox.Every pointee in practice has to have some sort of defined layout, and as pointers exist in a finite space, layout is also guaranteed to be finite. rust-lang/rfcs#3536 offers a partial solution for sizes, but doesn't address the two cases where alignment isn't statically known: the dynamic alignment of
dyn Traitand the undefined alignment of opaque types.So,
ptr::Pointeeneeds to expose a way to get this information, as it was initially intended to. There's a few constraints:The simplest way I can think of is unfortunately still somewhat involved, and I'm not sure I can reduce it any further:
Add a required
fn layout(&self) -> alloc::Layoutmethod toptr::Pointee.Add a
marker::Alignedtrait similar tomarker::Sized, representing that the type has a statically defined alignment. Almost everything auto-implements this, butdyn Traitand opaque types will not.Sizedwill be modified to subtype this. The only way to declare an unknown alignment is to use anexterntype, a type of DST, and so it's not possible to both make the size statically determinable and leave the alignment unknown.Like its counterpart
Sized,Alignedmust be implemented to read something by value, and generic parameters implicitly add anAlignedconstraint unless you explicitly add?Aligned. Unlike it,Alignedmay be manually implemented forexterntypes via a#[repr(align(N))]attribute.Make
Pointeeanunsafetrait. The lack of safety is because it's defining the memory safety boundaries.The
layoutmethod is safe to call, but the method itself must ensure that:T.&selfpointer.Auto-implement
Pointeefor every type that either implementsAlignedor has a final field that implementsPointee.If the final field does not implement
Alignedbut does implementPointee, the auto-generated implementation returns essentiallystatic_prefix.extend(final_field.layout()).unwrap().0.Generics will assume
Pointeeto be implemented by default. It'll be likeSizedrather thanUnpinin that regard.Here's how it'd look for the bulk of this at the type level:
There's a couple other benefits this provides:
size_of_valandalign_of_val. Their implementations are of course in the above code block.layouts against the struct's backing memory on construct and on every pointer cast to the type, ensuring the referenced memory region is always valid and that the pointer in question is correctly aligned.