From 0bb757ee33001b26f7ad122d5228b2f43a41c04e Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 25 Oct 2018 19:03:20 +0200 Subject: [PATCH 01/31] RFC: DynTrait and VTable --- text/0000-vtable.md | 308 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 text/0000-vtable.md diff --git a/text/0000-vtable.md b/text/0000-vtable.md new file mode 100644 index 00000000000..8e21e8f7d83 --- /dev/null +++ b/text/0000-vtable.md @@ -0,0 +1,308 @@ +- Feature Name: `vtable` +- Start Date: 2018-10-24 +- RFC PR: +- Rust Issue: + +# Summary +[summary]: #summary + +Add a `DynTrait` trait and a `VTable` type to provide +access to the components of a fat pointer to a trait object `dyn SomeTrait`, +and the ability to reconstruct such a pointer from its components. + + +# Background +[background]: #background + +## Dynamically-sized types (DSTs) + +Any Rust type `T` is either statically-sized or dynamically-sized. + +All values of a statically-sized types occupy the same size in memory. +This size is known at compile-time. +Raw pointers or references `*const T`, `*mut T`, `&T`, `&mut T` to such types +are represented in memory as a single pointer. + +Different values of a same DST (dynamically-sized type) can have a different size +which can be queried at runtime. +Raw pointers and references to DSTs are “fat” pointers made of one data pointer to the value +together with some metadata. +In Rust 1.30, a DST is always one of: + +* A struct whose last field is a DST (and is the only DST field), + where the metadata is the same as for a reference to that field. +* A slice type like `[U]`, where the metadata is the length in items, +* The string slice type `str`, where the metadata is the length in bytes, +* Or a trait object like `dyn SomeTrait`, + where the metadata is a pointer to a vtable (virtutal call table). + A vtable contains the size and required alignment of the concrete type, + a function pointer to the destructor if any, + and pointers for the implementations of the trait’s methods. + +## Fat pointer components + +Typical high-level code doesn’t need to worry about this, +a reference `&Foo` “just works” wether or not `Foo` is a DST. +But unsafe code such as a custom collection library may want to access a fat pointer’s +components separately. + +In Rust 1.11 we *removed* a [`std::raw::Repr`] trait and a [`std::raw::Slice`] type +from the standard library. +`Slice` could be `transmute`d to a `&[U]` or `&mut [U]` reference to a slice +as it was guaranteed to have the same memory layout. +This was replaced with more specific and less wildly unsafe +`std::slice::from_raw_parts` and `std::slice::from_raw_parts_mut` functions, +together with `as_ptr` and `len` methods that extract each fat pointer component separatly. + +The `str` type is taken care of by APIs to convert to and from `[u8]`. + +This leaves trait objects, where we still have an unstable `std::raw::TraitObjet` type +that can only be used with `transmute`: + +```rust +#[repr(C)] +pub struct TraitObject { + pub data: *mut (), + pub vtable: *mut (), +} +``` + +[`std::raw::Repr`]: https://doc.rust-lang.org/1.10.0/std/raw/trait.Repr.html +[`std::raw::Slice`]: https://doc.rust-lang.org/1.10.0/std/raw/struct.Slice.html +[`std::raw::TraitObjet`]: https://doc.rust-lang.org/1.30.0/std/raw/struct.TraitObject.html + +## Trait objects of multiple traits + +A trait object type can refer to multiple traits like `dyn A + B`, `dyn A + B + C`, etc. +Currently all traits after the first must be [auto traits]. +Since auto traits cannot have methods, they don’t require additional data in the vtable. + +Lifting this restriction is desirable, and has been discussed in +[RFC issue #2035][2035] and [Internals thread #6617][6617]. +Two related open questions with this is how to represent the vtable of such a trait object, +and how to support upcasting: +converting for example from `Box` to `Box`. + +One possibility is having “super-fat” pointers +whose metadata is made of multiple pointers to separate vtables. +However making the size of pointers grow linearly with the number of traits involved +is a serious downside. + +[auto traits]: https://doc.rust-lang.org/1.30.0/reference/special-types-and-traits.html#auto-traits +[2035]: https://github.com/rust-lang/rfcs/issues/2035 +[6617]: https://internals.rust-lang.org/t/wheres-the-catch-with-box-read-write/6617 + + +# Motivation +[motivation]: #motivation + +We now have APIs in Stable Rust to let unsafe code freely and reliably manipulate slices, +accessing the separate components of a fat pointers and then re-assembling them. +However `std::raw::TraitObject` is still unstable, +but it’s probably not the style of API that we’ll want to stabilize +as at encourages dangerous `transmute` calls. +This is a “hole” in available APIs to manipulate existing Rust types. + +For example [this library][lib] stores multiple trait objects of varying size +in contiguous memory together with their vtable pointers, +and during iteration recreates fat pointers from separate data and vtable pointers. + +[lib]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2015&gist=bbeecccc025f5a7a0ad06086678e13f3 + + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +For low-level manipulation of trait objects in unsafe code, +the `DynTrait` trait allows accessing the components of a fat pointer: + +```rust +use std::dyn_trait::{DynTrait, VTable}; + +fn callback_into_raw_parts(f: Box) -> (*const (), &'static VTable) { + let raw = Box::into_raw(f); + (DynTrait::data_ptr(raw), DynTrait::vtable(raw)) +} +``` + +… and assembling it back again: + +```rust +fn callback_from_raw_parts(data: *const (), vtable: &'static VTable) -> Box { + let raw: *const Fn() = DynTrait::from_raw_parts(new_data, vtable); + Box::from_raw(raw as *mut Fn()) +} +``` + +`VTable` also provides enough information to manage memory allocation: + +```rust +// Equivalent to just letting `Box` go out of scope to have its destructor run, +// this only demonstrates some APIs. +fn drop_callback(f: Box) { + let raw = Box::into_raw(f); + let vtable = DynTrait::vtable(raw); + unsafe { + // `DynTrait::data` is equivalent to casting to a thin pointer with `as` + vtable.drop_in_place(raw as *mut ()); + std::alloc::dealloc(raw as *mut u8, vtable.layout()); + } +} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +A new `core::dyn_trait` module is added and re-exported as `std::dyn_trait`. +Its contains a `DynTrait` trait and `VTable` pointer whose definitions are given below. + +`DynTrait` is automatically implement by the language / compiler +(similar to `std::marker::Unsize`, with explicit impls causing a E0328 error) +for all existing trait object types (DSTs) +like `dyn SomeTrait`, `dyn SomeTrait + SomeAutoTrait`, etc. + +If in the future “super-fat” pointers (with multiple separate vtable pointers as DST metadata) +are added to the language to refer to trait objects with multiple non-auto traits, +`DynTrait` will **not** be implemented for such trait object types. + +`std::raw::TraitObject` and `std::raw` are deprecated and eventually removed. + +```rust +/// A trait implemented by any type that is a trait object with a single vtable. +/// +/// This allows generic code to constrain itself to trait-objects, and subsequently +/// enables code to access the vtable directly and store trait-object values inline. +/// +/// For instance `dyn Display` implements `Trait`. +#[lang = "dyn_trait"] +pub trait DynTrait: ?Sized { + /// Extracts the data pointer from a trait object. + fn data_ptr(obj: *const Self) -> *const () { obj as _ } + + /// Extracts the vtable pointer from a trait object. + fn vtable(obj: *const Self) -> &'static VTable; + + /// Creates a trait object from its data and vtable pointers. + /// + /// Undefined Behaviour will almost certainly result if the data pointed + /// to isn’t a valid instance of the type the vtable is associated with. + unsafe fn from_raw_parts(data: *const (), vtable: &'static VTable) -> *const Self; +} + +/// The vtable for a trait object. +/// +/// A vtable (virtual call table) represents all the necessary information +/// to manipulate the concrete type stored inside a trait object. +/// It notably it contains: +/// +/// * type size +/// * type alignment +/// * a pointer to the type’s `drop_in_place` impl (may be a no-op for plain-old-data) +/// * pointers to all the methods for the type’s implementation of the trait +/// +/// Note that the first three are special because they’re necessary to allocate, drop, +/// and deallocate any trait object. +/// +/// The layout of vtables is still unspecified, so this type is one a more-type-safe +/// convenience for accessing those 3 special values. Note however that `VTable` does +/// not actually know the trait it’s associated with, indicating that, at very least, +/// the location of `size`, `align`, and `drop_in_place` is identical for all +/// trait object vtables in a single program. +pub struct VTable { + _priv: (), +} + +impl VTable { + /// Returns the size of the type associated with this vtable. + pub fn size(&self) -> usize { ... } + + /// Returns the alignment of the type associated with this vtable. + pub fn align(&self) -> usize { ... } + + /// Returns the size and alignment together as a `Layout` + pub fn layout(&self) -> alloc::Layout { + unsafe { + alloc::Layout::from_size_align_unchecked(self.size(), self.align()) + } + } + + /// Drops the value pointed at by `data` assuming it has the type + /// associated with this vtable. + /// + /// Behaviour is Undefined if the type isn’t correct or the pointer + /// is invalid to reinterpret as `&mut TheType`. + pub unsafe fn drop_in_place(&self, data: *mut ()) { ... } +} +``` + +# Drawbacks +[drawbacks]: #drawbacks + +If super-fat pointers are ever added to the language, this API will not work with them +and another, somewhat-redundant API will be needed. + +The RFC as proposed also prevents us from ever changing +trait objects of a trait that has multiple super-traits +to using super-fat pointers. + +```rust +trait A {} +trait B {} +trait C {} +trait Foo: A + B + C {} +let pointer_size = std::mem::size_of::<*const ()>(); +// This holds as of Rust 1.30: +assert_eq!(std::mem::size_of::<&dyn Foo>(), 2 * pointer_size); +``` + +The author opinion is that the size cost for pointers (which can be copied or moved around a lot) +makes super-fat pointers prohibitive and that a different solution would be preferable, +regardless of this RFC. + + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +The status quo is that code (such as linked in [Motivation]) that requires this functionality +needs to transmute to and from `std::raw::TraitObject` +or a copy of it (to be compatible with Stable Rust). +Additionally, in cases where constructing the data pointer +requires knowing the alignment of the concrete type, +a dangling pointer such as `0x8000_0000_usize as *mut ()` needs to be created. +It is not clear whether `std::mem::align_of(&*ptr)` with `ptr: *const dyn SomeTrait` +is Undefined Behavior with a dangling data pointer. + +Support for [Custom DSTs] would include functionality equivalent to the `DynTrait` trait. +But it has been postponed, apparently indefinitely. +It is a much more general feature that involves significant design and implementation work. + +An itermediate solution might be to generalize `DynTrait` to a trait implemented for *all* types, +with an associated type for the metadata in pointers and references. +(This metadata would be `()` for thin pointers.) +This trait’s methods would be the same as in `DynTrait`, +with `Self::Metadata` instead of `&'static VTable`. +At first this trait would only be implemented automatically, +so libraries would not be able to create new kinds of DSTs, +but this design might be extensible in that direction later. + +To allow for the possiblity of +changing trait objects with multiple super-traits to use super-fat pointers later, +we could make such trait objects not implement `DynTrait` at first. +Specifically: among all the traits and recursive super-traits involved in a trait object, +only implement `DynTrait` if each trait has at most one non-auto (or non-[marker]) super-trait. + +[Custom DSTs]: https://internals.rust-lang.org/t/custom-dst-discussion/4842 +[marker]: https://github.com/rust-lang/rust/issues/29864 + + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +* Is a new `dyn_trait` module inside `core` and `std` the appropriate location? + `trait` would be nice, but that’s a reserved keyword. + `traits` would work but there is not much precedent for pluralizing module names in this way. + (For example `std::slice`, `std::string`, …) + +* Every item name is of course also subject to the usual bikeshed. + +* `*const ()` v.s. `*mut ()`? \ No newline at end of file From d4acf9b4b1f8559842794bfb1b2d1c3255948a8b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 26 Oct 2018 15:59:05 +0200 Subject: [PATCH 02/31] Rename to vtable RFC to ptr-meta --- text/{0000-vtable.md => 0000-ptr-meta.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename text/{0000-vtable.md => 0000-ptr-meta.md} (100%) diff --git a/text/0000-vtable.md b/text/0000-ptr-meta.md similarity index 100% rename from text/0000-vtable.md rename to text/0000-ptr-meta.md From 8de354cd895f98134afe2bc25f2d03d948fdcb01 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 26 Oct 2018 18:11:17 +0200 Subject: [PATCH 03/31] RFC: Pointer metadata --- text/0000-ptr-meta.md | 286 ++++++++++++++++++++++-------------------- 1 file changed, 149 insertions(+), 137 deletions(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 8e21e8f7d83..9d2f744eb01 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -1,47 +1,26 @@ -- Feature Name: `vtable` -- Start Date: 2018-10-24 +- Feature Name: `ptr-meta` +- Start Date: 2018-10-26 - RFC PR: - Rust Issue: # Summary [summary]: #summary -Add a `DynTrait` trait and a `VTable` type to provide -access to the components of a fat pointer to a trait object `dyn SomeTrait`, -and the ability to reconstruct such a pointer from its components. +Add generic APIs that allow manipulating the metadata of fat pointers: +* Naming the metadata’s type (as an associated type) +* Extracting metadata from a pointer +* Reconstructing a pointer from a data pointer and metadata +* Representing vtables, the metadata for trait objects, as a type with some limited API -# Background -[background]: #background - -## Dynamically-sized types (DSTs) - -Any Rust type `T` is either statically-sized or dynamically-sized. - -All values of a statically-sized types occupy the same size in memory. -This size is known at compile-time. -Raw pointers or references `*const T`, `*mut T`, `&T`, `&mut T` to such types -are represented in memory as a single pointer. - -Different values of a same DST (dynamically-sized type) can have a different size -which can be queried at runtime. -Raw pointers and references to DSTs are “fat” pointers made of one data pointer to the value -together with some metadata. -In Rust 1.30, a DST is always one of: +This RFC does *not* propose a mechanism for defining custom dynamically-sized types, +but tries to stay compatible with future proposals that do. -* A struct whose last field is a DST (and is the only DST field), - where the metadata is the same as for a reference to that field. -* A slice type like `[U]`, where the metadata is the length in items, -* The string slice type `str`, where the metadata is the length in bytes, -* Or a trait object like `dyn SomeTrait`, - where the metadata is a pointer to a vtable (virtutal call table). - A vtable contains the size and required alignment of the concrete type, - a function pointer to the destructor if any, - and pointers for the implementations of the trait’s methods. -## Fat pointer components +# Background +[background]: #background -Typical high-level code doesn’t need to worry about this, +Typical high-level code doesn’t need to worry about fat pointers, a reference `&Foo` “just works” wether or not `Foo` is a DST. But unsafe code such as a custom collection library may want to access a fat pointer’s components separately. @@ -54,9 +33,7 @@ This was replaced with more specific and less wildly unsafe `std::slice::from_raw_parts` and `std::slice::from_raw_parts_mut` functions, together with `as_ptr` and `len` methods that extract each fat pointer component separatly. -The `str` type is taken care of by APIs to convert to and from `[u8]`. - -This leaves trait objects, where we still have an unstable `std::raw::TraitObjet` type +For trait objects, where we still have an unstable `std::raw::TraitObjet` type that can only be used with `transmute`: ```rust @@ -71,27 +48,6 @@ pub struct TraitObject { [`std::raw::Slice`]: https://doc.rust-lang.org/1.10.0/std/raw/struct.Slice.html [`std::raw::TraitObjet`]: https://doc.rust-lang.org/1.30.0/std/raw/struct.TraitObject.html -## Trait objects of multiple traits - -A trait object type can refer to multiple traits like `dyn A + B`, `dyn A + B + C`, etc. -Currently all traits after the first must be [auto traits]. -Since auto traits cannot have methods, they don’t require additional data in the vtable. - -Lifting this restriction is desirable, and has been discussed in -[RFC issue #2035][2035] and [Internals thread #6617][6617]. -Two related open questions with this is how to represent the vtable of such a trait object, -and how to support upcasting: -converting for example from `Box` to `Box`. - -One possibility is having “super-fat” pointers -whose metadata is made of multiple pointers to separate vtables. -However making the size of pointers grow linearly with the number of traits involved -is a serious downside. - -[auto traits]: https://doc.rust-lang.org/1.30.0/reference/special-types-and-traits.html#auto-traits -[2035]: https://github.com/rust-lang/rfcs/issues/2035 -[6617]: https://internals.rust-lang.org/t/wheres-the-catch-with-box-read-write/6617 - # Motivation [motivation]: #motivation @@ -107,21 +63,26 @@ For example [this library][lib] stores multiple trait objects of varying size in contiguous memory together with their vtable pointers, and during iteration recreates fat pointers from separate data and vtable pointers. +The new `Thin` trait alias also expanding to [extern types] some APIs +that were unnecessarily restricted to `Sized` types +because there was previously no way to express pointen-thinness in generic code. + [lib]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2015&gist=bbeecccc025f5a7a0ad06086678e13f3 # Guide-level explanation [guide-level-explanation]: #guide-level-explanation + For low-level manipulation of trait objects in unsafe code, -the `DynTrait` trait allows accessing the components of a fat pointer: +new APIs allow accessing the components of a fat pointer: ```rust -use std::dyn_trait::{DynTrait, VTable}; +use std::ptr::{metadata, VTable}; fn callback_into_raw_parts(f: Box) -> (*const (), &'static VTable) { let raw = Box::into_raw(f); - (DynTrait::data_ptr(raw), DynTrait::vtable(raw)) + (raw as _, metadata(raw)) } ``` @@ -129,21 +90,19 @@ fn callback_into_raw_parts(f: Box) -> (*const (), &'static VTable) { ```rust fn callback_from_raw_parts(data: *const (), vtable: &'static VTable) -> Box { - let raw: *const Fn() = DynTrait::from_raw_parts(new_data, vtable); - Box::from_raw(raw as *mut Fn()) + Box::from_raw(<*mut Fn()>::from_raw_parts(new_data, vtable)) } ``` `VTable` also provides enough information to manage memory allocation: ```rust -// Equivalent to just letting `Box` go out of scope to have its destructor run, +// Equivalent to letting `Box` go out of scope to have its destructor run, // this only demonstrates some APIs. fn drop_callback(f: Box) { let raw = Box::into_raw(f); - let vtable = DynTrait::vtable(raw); + let vtable = metadata(raw); unsafe { - // `DynTrait::data` is equivalent to casting to a thin pointer with `as` vtable.drop_in_place(raw as *mut ()); std::alloc::dealloc(raw as *mut u8, vtable.layout()); } @@ -153,40 +112,99 @@ fn drop_callback(f: Box) { # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -A new `core::dyn_trait` module is added and re-exported as `std::dyn_trait`. -Its contains a `DynTrait` trait and `VTable` pointer whose definitions are given below. +(Parts of this assumes that [trait aliases] are implemented. +If they are not implemented by the time this RFC is, +the `Thin` alias can be replaced by its definition.) + +The APIs whose full definition is found below +are added to `core::ptr` and re-exported in `std::ptr`: + +* A `Pointee` trait, + implemented automatically for all types + (similar to how `Sized` and `Unsize` are implemented automatically). +* A `Thin` [trait alias]. + If this RFC is implemented before type aliases are, + uses of `Thin` should be replaced with its definition. +* A `metadata` free function +* A `from_raw_parts` constructor for each of `*const T` and `*mut T` -`DynTrait` is automatically implement by the language / compiler -(similar to `std::marker::Unsize`, with explicit impls causing a E0328 error) -for all existing trait object types (DSTs) -like `dyn SomeTrait`, `dyn SomeTrait + SomeAutoTrait`, etc. +The bounds on `null()` and `null_mut()` function in that same module +as well as the `NonNull::dangling` constructor +are changed from (implicit) `T: Sized` to `T: ?Sized + Thin`. +This enables using those functions with [extern types]. -If in the future “super-fat” pointers (with multiple separate vtable pointers as DST metadata) -are added to the language to refer to trait objects with multiple non-auto traits, -`DynTrait` will **not** be implemented for such trait object types. +For the purpose of pointer casts being allowed by the `as` operator, +a pointer to `T` is considered to be thin if `T: Thin` instead of `T: Sized`. +This similarly includes extern types. `std::raw::TraitObject` and `std::raw` are deprecated and eventually removed. +[trait alias]: https://github.com/rust-lang/rust/issues/41517 +[extern types]: https://github.com/rust-lang/rust/issues/43467 + ```rust -/// A trait implemented by any type that is a trait object with a single vtable. +/// This trait is automatically implement for every type. +/// +/// Raw pointer types and referenece types in Rust can be thought of as made of two parts: +/// a data pointer that contains the memory address of the value, and some metadata. +/// +/// For statically-sized types that implement the `Sized` traits, +/// pointers are said to be “thin”, metadata is zero-size, and metadata’s type is `()`. +/// +/// Pointers to [dynamically-sized types][dst] are said to be “fat” +/// and have non-zero-size metadata: +/// +/// * For structs whose last field is a DST, metadata is the metadata for the last field +/// * For the `str` type, metadata is the length in bytes as `usize` +/// * For slice types like `[T]`, metadata is the length in items as `usize` +/// * For trait objects like `dyn SomeTrait`, metadata is [`&'static VTable`][VTable] +/// +/// In the future, the Rust language may gain new kinds of types +/// that have different pointer metadata. /// -/// This allows generic code to constrain itself to trait-objects, and subsequently -/// enables code to access the vtable directly and store trait-object values inline. +/// Pointer metadata can be extracted from a pointer or reference with the [`metadata`] function. +/// The data pointer can be extracted by casting a (fat) pointer +/// to a (thin) pointer to a `Sized` type the `as` operator, +/// for example `(x: &dyn SomeTrait) as *const SomeTrait as *const ()`. /// -/// For instance `dyn Display` implements `Trait`. -#[lang = "dyn_trait"] -pub trait DynTrait: ?Sized { - /// Extracts the data pointer from a trait object. - fn data_ptr(obj: *const Self) -> *const () { obj as _ } +/// [dst]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts +#[lang = "pointee"] +pub trait Pointee { + /// The type for metadata in pointers and references to `Self`. + type Metadata; +} - /// Extracts the vtable pointer from a trait object. - fn vtable(obj: *const Self) -> &'static VTable; +/// Pointers to types implementing this trait alias are +pub trait Thin = Pointee; - /// Creates a trait object from its data and vtable pointers. - /// - /// Undefined Behaviour will almost certainly result if the data pointed - /// to isn’t a valid instance of the type the vtable is associated with. - unsafe fn from_raw_parts(data: *const (), vtable: &'static VTable) -> *const Self; +/// Extract the metadata component of a pointer. +/// +/// Values of type `*mut T`, `&T`, or `&mut T` can be passed directly to this function +/// as they implicitly coerce to `*const T`. +/// For example: +/// +/// ``` +/// assert_eq(std::ptr::metadata("foo"), 3_usize); +/// ``` +/// +/// Note that the data component of a (fat) pointer can be extracted by casting +/// to a (thin) pointer to any `Sized` type: +/// +/// ``` +/// # trait SomeTrait {} +/// # fn example(something: &SomeTrait) { +/// let object: &SomeTrait = something; +/// let data_ptr = object as *const SomeTrait as *const (); +/// # } +/// ``` +pub fn metadata(ptr: *const T) -> ::Metadata {…} + +impl *const T { + pub fn from_raw_parts(data: *const (), meta: ::Metadata) -> Self {…} +} + +impl *mut T { + pub fn from_raw_parts(data: *mut (), meta: ::Metadata) -> Self {…} } /// The vtable for a trait object. @@ -235,30 +253,6 @@ impl VTable { } ``` -# Drawbacks -[drawbacks]: #drawbacks - -If super-fat pointers are ever added to the language, this API will not work with them -and another, somewhat-redundant API will be needed. - -The RFC as proposed also prevents us from ever changing -trait objects of a trait that has multiple super-traits -to using super-fat pointers. - -```rust -trait A {} -trait B {} -trait C {} -trait Foo: A + B + C {} -let pointer_size = std::mem::size_of::<*const ()>(); -// This holds as of Rust 1.30: -assert_eq!(std::mem::size_of::<&dyn Foo>(), 2 * pointer_size); -``` - -The author opinion is that the size cost for pointers (which can be copied or moved around a lot) -makes super-fat pointers prohibitive and that a different solution would be preferable, -regardless of this RFC. - # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives @@ -272,37 +266,55 @@ a dangling pointer such as `0x8000_0000_usize as *mut ()` needs to be created. It is not clear whether `std::mem::align_of(&*ptr)` with `ptr: *const dyn SomeTrait` is Undefined Behavior with a dangling data pointer. -Support for [Custom DSTs] would include functionality equivalent to the `DynTrait` trait. -But it has been postponed, apparently indefinitely. -It is a much more general feature that involves significant design and implementation work. +A [previous iteration][2579] of this RFC proposed a `DynTrait` +that would only be implemented for trait objects like `dyn SomeTrait`. +There would be no `Metadata` associated type, `&'static VTable` was hard-coded in the trait. +In addition to being more general +and (hopefully) more compatible with future custom DSTs proposals, +this RFC resolves the question of what happens +if trait objects with super-fat pointers with multiple vtable pointers are ever added. +(Answer: they can use a different metadata type like `[&'static VTable; N]`.) + +`VTable` could be made generic with a type parameter for the trait object type that it describes. +This would avoid forcing that the size, alignment, and destruction pointers +be in the same location (offset) for every vtables. +But keeping them in the same location is probaly desirable anyway to keep code size + +[2579]: https://github.com/rust-lang/rfcs/pull/2579 + -An itermediate solution might be to generalize `DynTrait` to a trait implemented for *all* types, -with an associated type for the metadata in pointers and references. -(This metadata would be `()` for thin pointers.) -This trait’s methods would be the same as in `DynTrait`, -with `Self::Metadata` instead of `&'static VTable`. -At first this trait would only be implemented automatically, -so libraries would not be able to create new kinds of DSTs, -but this design might be extensible in that direction later. +# Prior art +[prior-art]: #prior-art -To allow for the possiblity of -changing trait objects with multiple super-traits to use super-fat pointers later, -we could make such trait objects not implement `DynTrait` at first. -Specifically: among all the traits and recursive super-traits involved in a trait object, -only implement `DynTrait` if each trait has at most one non-auto (or non-[marker]) super-trait. +A previous [Custom Dynamically-Sized Types][cdst] RFC was postponed. +[Internals thread #6663][6663] took the same ideas +and was even more ambitious in being very general. +Except for `VTable`’s methods, this RFC proposes a subset of what that thread did. -[Custom DSTs]: https://internals.rust-lang.org/t/custom-dst-discussion/4842 -[marker]: https://github.com/rust-lang/rust/issues/29864 +[cdst]: https://github.com/rust-lang/rfcs/pull/1524 +[6663]: https://internals.rust-lang.org/t/pre-erfc-lets-fix-dsts/6663 # Unresolved questions [unresolved-questions]: #unresolved-questions -* Is a new `dyn_trait` module inside `core` and `std` the appropriate location? - `trait` would be nice, but that’s a reserved keyword. - `traits` would work but there is not much precedent for pluralizing module names in this way. - (For example `std::slice`, `std::string`, …) +* The name of `Pointee`. [Internals thread #6663][6663] used `Referent`. + +* The location of `VTable`. Is another module more appropriate than `std::ptr`? + +* Should `VTable` be an extern type? + Rather than a zero-size struct? Actual vtables vary in size, + but `VTable` presumably is never used exept behind `&'static`. + +* The name of `Thin`. + This name is short and sweet but `T: Thin` suggests that `T` itself is thin, + rather than pointers and references to `T`. + +* The location of `Thin`. Better in `std::marker`? + +* Should `Thin` be added as a supertrait of `Sized`? + Or could it ever make sense to have fat pointers to statically-sized types? -* Every item name is of course also subject to the usual bikeshed. +* Are there other generic standard library APIs like `ptr::null()` + that have an (implicit) `T: Sized` bound that unneccesarily excludes extern types? -* `*const ()` v.s. `*mut ()`? \ No newline at end of file From 24a2ff8c21996badd3504b0840c9ea43b80dc630 Mon Sep 17 00:00:00 2001 From: kennytm Date: Sun, 28 Oct 2018 12:23:43 +0100 Subject: [PATCH 04/31] Pointer metadata: add bounds on the associated type Co-Authored-By: SimonSapin --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 9d2f744eb01..a4ee45f3a4c 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -171,7 +171,7 @@ This similarly includes extern types. #[lang = "pointee"] pub trait Pointee { /// The type for metadata in pointers and references to `Self`. - type Metadata; + type Metadata: Copy + Send + Sync + Ord + Hash + 'static; } /// Pointers to types implementing this trait alias are From dd3de2f4de19dac97703e35ec054f669eb07ffc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20S=CC=B6c=CC=B6h=CC=B6n=CC=B6e=CC=B6i=CC=B6d=CC=B6?= =?UTF-8?q?e=CC=B6r=20Scherer?= Date: Sun, 28 Oct 2018 12:24:27 +0100 Subject: [PATCH 05/31] Pointer metadata: typo fix Co-Authored-By: SimonSapin --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index a4ee45f3a4c..87f560e473b 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -56,7 +56,7 @@ We now have APIs in Stable Rust to let unsafe code freely and reliably manipulat accessing the separate components of a fat pointers and then re-assembling them. However `std::raw::TraitObject` is still unstable, but it’s probably not the style of API that we’ll want to stabilize -as at encourages dangerous `transmute` calls. +as it encourages dangerous `transmute` calls. This is a “hole” in available APIs to manipulate existing Rust types. For example [this library][lib] stores multiple trait objects of varying size From 637647f6f33db88f7763a44ab5f54723ef621afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20S=CC=B6c=CC=B6h=CC=B6n=CC=B6e=CC=B6i=CC=B6d=CC=B6?= =?UTF-8?q?e=CC=B6r=20Scherer?= Date: Sun, 28 Oct 2018 12:24:42 +0100 Subject: [PATCH 06/31] Pointer metadata: typo fix Co-Authored-By: SimonSapin --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 87f560e473b..d2a418a2a4b 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -33,7 +33,7 @@ This was replaced with more specific and less wildly unsafe `std::slice::from_raw_parts` and `std::slice::from_raw_parts_mut` functions, together with `as_ptr` and `len` methods that extract each fat pointer component separatly. -For trait objects, where we still have an unstable `std::raw::TraitObjet` type +For trait objects, where we still have an unstable `std::raw::TraitObject` type that can only be used with `transmute`: ```rust From 99fbed9d704606c793420f30baa9ff17d5f7a6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20S=CC=B6c=CC=B6h=CC=B6n=CC=B6e=CC=B6i=CC=B6d=CC=B6?= =?UTF-8?q?e=CC=B6r=20Scherer?= Date: Sun, 28 Oct 2018 12:24:53 +0100 Subject: [PATCH 07/31] Pointer metadata: typo fix Co-Authored-By: SimonSapin --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index d2a418a2a4b..69ebc470119 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -65,7 +65,7 @@ and during iteration recreates fat pointers from separate data and vtable pointe The new `Thin` trait alias also expanding to [extern types] some APIs that were unnecessarily restricted to `Sized` types -because there was previously no way to express pointen-thinness in generic code. +because there was previously no way to express pointer-thinness in generic code. [lib]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2015&gist=bbeecccc025f5a7a0ad06086678e13f3 From 74e185c115fccd40e38385a82caeaa1039f3e441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20S=CC=B6c=CC=B6h=CC=B6n=CC=B6e=CC=B6i=CC=B6d=CC=B6?= =?UTF-8?q?e=CC=B6r=20Scherer?= Date: Sun, 28 Oct 2018 12:25:03 +0100 Subject: [PATCH 08/31] Pointer metadata: typo fix Co-Authored-By: SimonSapin --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 69ebc470119..86d58a1cd26 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -145,7 +145,7 @@ This similarly includes extern types. ```rust /// This trait is automatically implement for every type. /// -/// Raw pointer types and referenece types in Rust can be thought of as made of two parts: +/// Raw pointer types and reference types in Rust can be thought of as made of two parts: /// a data pointer that contains the memory address of the value, and some metadata. /// /// For statically-sized types that implement the `Sized` traits, From 65418d0441ce2ef5040d0741f62c8889551c7f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20S=CC=B6c=CC=B6h=CC=B6n=CC=B6e=CC=B6i=CC=B6d=CC=B6?= =?UTF-8?q?e=CC=B6r=20Scherer?= Date: Sun, 28 Oct 2018 12:25:25 +0100 Subject: [PATCH 09/31] Pointer metadata: grammar Co-Authored-By: SimonSapin --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 86d58a1cd26..246166361c3 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -149,7 +149,7 @@ This similarly includes extern types. /// a data pointer that contains the memory address of the value, and some metadata. /// /// For statically-sized types that implement the `Sized` traits, -/// pointers are said to be “thin”, metadata is zero-size, and metadata’s type is `()`. +/// pointers are said to be “thin”, metadata is zero-sized, and metadata’s type is `()`. /// /// Pointers to [dynamically-sized types][dst] are said to be “fat” /// and have non-zero-size metadata: From ef9ee3265797a7033985cc1139ed2f9482cd7f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20S=CC=B6c=CC=B6h=CC=B6n=CC=B6e=CC=B6i=CC=B6d=CC=B6?= =?UTF-8?q?e=CC=B6r=20Scherer?= Date: Sun, 28 Oct 2018 12:25:33 +0100 Subject: [PATCH 10/31] Pointer metadata: grammar Co-Authored-By: SimonSapin --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 246166361c3..1302d721538 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -152,7 +152,7 @@ This similarly includes extern types. /// pointers are said to be “thin”, metadata is zero-sized, and metadata’s type is `()`. /// /// Pointers to [dynamically-sized types][dst] are said to be “fat” -/// and have non-zero-size metadata: +/// and have non-zero-sized metadata: /// /// * For structs whose last field is a DST, metadata is the metadata for the last field /// * For the `str` type, metadata is the length in bytes as `usize` From f6d95d7d4a269a5432885282e6d5d929538b678e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20S=CC=B6c=CC=B6h=CC=B6n=CC=B6e=CC=B6i=CC=B6d=CC=B6?= =?UTF-8?q?e=CC=B6r=20Scherer?= Date: Sun, 28 Oct 2018 12:26:47 +0100 Subject: [PATCH 11/31] Pointer metadata: grammar Co-Authored-By: SimonSapin --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 1302d721538..1055de74241 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -277,7 +277,7 @@ if trait objects with super-fat pointers with multiple vtable pointers are ever `VTable` could be made generic with a type parameter for the trait object type that it describes. This would avoid forcing that the size, alignment, and destruction pointers -be in the same location (offset) for every vtables. +be in the same location (offset) for every vtable. But keeping them in the same location is probaly desirable anyway to keep code size [2579]: https://github.com/rust-lang/rfcs/pull/2579 From e1aa0df3033ba5eff70ae1d4bf0612adec22735a Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 28 Oct 2018 12:44:52 +0100 Subject: [PATCH 12/31] Pointer metadata: fix unfinished sentences --- text/0000-ptr-meta.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 1055de74241..b5a1fe54707 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -174,7 +174,13 @@ pub trait Pointee { type Metadata: Copy + Send + Sync + Ord + Hash + 'static; } -/// Pointers to types implementing this trait alias are +/// Pointers to types implementing this trait alias are “thin”: +/// +/// ```rust +/// fn always_true() -> bool { +/// assert_eq(std::mem::size_of::<&T>(), std::mem::size_of::()) +/// } +/// ``` pub trait Thin = Pointee; /// Extract the metadata component of a pointer. @@ -278,7 +284,7 @@ if trait objects with super-fat pointers with multiple vtable pointers are ever `VTable` could be made generic with a type parameter for the trait object type that it describes. This would avoid forcing that the size, alignment, and destruction pointers be in the same location (offset) for every vtable. -But keeping them in the same location is probaly desirable anyway to keep code size +But keeping them in the same location is probaly desirable anyway to keep code size small. [2579]: https://github.com/rust-lang/rfcs/pull/2579 From 18f1fef8c3eff4443b2086df5d88f9b1ca568b6d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 28 Oct 2018 14:29:25 +0100 Subject: [PATCH 13/31] Pointer metadata: remove paragraph made redundant --- text/0000-ptr-meta.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index b5a1fe54707..784d30aa3fc 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -112,10 +112,6 @@ fn drop_callback(f: Box) { # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -(Parts of this assumes that [trait aliases] are implemented. -If they are not implemented by the time this RFC is, -the `Thin` alias can be replaced by its definition.) - The APIs whose full definition is found below are added to `core::ptr` and re-exported in `std::ptr`: From 3dd148471a22791deb0124e3e72012630a78c81c Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 31 Oct 2018 12:51:08 +0100 Subject: [PATCH 14/31] Pointer metadata: remove `VTable::drop_in_place`? --- text/0000-ptr-meta.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 784d30aa3fc..236e8d6f531 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -320,3 +320,6 @@ Except for `VTable`’s methods, this RFC proposes a subset of what that thread * Are there other generic standard library APIs like `ptr::null()` that have an (implicit) `T: Sized` bound that unneccesarily excludes extern types? +* Should `VTable::drop_in_place` be removed? + `vtable.drop_in_place(data_ptr)` is identical to + `<*mut _>::from_raw_parts(data_ptr, vtable).drop_in_place()` From 8ede2319886b7968728155ece8bd5bc017539e4c Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 31 Oct 2018 12:57:55 +0100 Subject: [PATCH 15/31] Pointer metadata: more NonNull API --- text/0000-ptr-meta.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 236e8d6f531..9ef52148ea6 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -127,6 +127,7 @@ are added to `core::ptr` and re-exported in `std::ptr`: The bounds on `null()` and `null_mut()` function in that same module as well as the `NonNull::dangling` constructor are changed from (implicit) `T: Sized` to `T: ?Sized + Thin`. +Similarly for the `U` type parameter of the `NonNull::cast` method. This enables using those functions with [extern types]. For the purpose of pointer casts being allowed by the `as` operator, @@ -209,6 +210,14 @@ impl *mut T { pub fn from_raw_parts(data: *mut (), meta: ::Metadata) -> Self {…} } +impl NonNull { + pub fn from_raw_parts(data: NonNull<()>, meta: ::Metadata) -> Self { + unsafe { + NonNull::new_unchecked(<*mut _>::from_raw_parts(data.as_ptr(), meta)) + } + } +} + /// The vtable for a trait object. /// /// A vtable (virtual call table) represents all the necessary information From fb6ebad7873a0928dc3f507a3b22d90036233cbc Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 31 Oct 2018 13:02:54 +0100 Subject: [PATCH 16/31] Pointer metadata: generic code can assume `T: Pointee`. --- text/0000-ptr-meta.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 9ef52148ea6..1284608cd86 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -130,6 +130,13 @@ are changed from (implicit) `T: Sized` to `T: ?Sized + Thin`. Similarly for the `U` type parameter of the `NonNull::cast` method. This enables using those functions with [extern types]. +The `Pointee` trait is implemented for all types. +This can be relied on in generic code, +even if a type parameter `T` does not have an explicit `T: Pointee` bound. +This is similar to how the `Any` trait can be used without an explicit `T: Any` bound, +only `T: 'static`, because a blanket `impl Any for T {…}` exists. +(Except that `Pointee` is not restricted to `'static`.) + For the purpose of pointer casts being allowed by the `as` operator, a pointer to `T` is considered to be thin if `T: Thin` instead of `T: Sized`. This similarly includes extern types. From bf801630c496ae71dda917d9969b4080da49216e Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 31 Oct 2018 15:55:47 +0100 Subject: [PATCH 17/31] Pointer metadata: rewrite Guide-level explanation with more useful example code --- text/0000-ptr-meta.md | 89 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 1284608cd86..4dc6dbfea70 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -74,41 +74,94 @@ because there was previously no way to express pointer-thinness in generic code. [guide-level-explanation]: #guide-level-explanation -For low-level manipulation of trait objects in unsafe code, -new APIs allow accessing the components of a fat pointer: +Let’s build generic type similar to `Box`, +but where the vtable pointer is stored in heap memory next to the value +so that the pointer is thin. +First, let’s get some boilerplate out of the way: ```rust -use std::ptr::{metadata, VTable}; +use std::marker::{PhantomData, Unsize}; +use std::ptr::{self, VTable}; -fn callback_into_raw_parts(f: Box) -> (*const (), &'static VTable) { - let raw = Box::into_raw(f); - (raw as _, metadata(raw)) +trait DynTrait = Pointee; + +pub struct ThinBox { + ptr: ptr::NonNull>, + phantom: PhantomData, +} + +#[repr(C)] +struct WithVTable { + vtable: &'static VTable, + value: T, } ``` -… and assembling it back again: +Since [unsized rvalues] are not implemented yet, +our constructor is going to “unsize” from a concrete type that implements our trait. +The `Unsize` bound ensures we can cast from `&S` to a `&Dyn` trait object +and construct the appopriate metadata. + +[unsized rvalues]: https://github.com/rust-lang/rust/issues/48055 + +We let `Box` do the memory layout computation and allocation: + +```rust +impl ThinBox { + pub fn new_unsize(value: S) -> Self where S: Unsize { + let vtable = ptr::metadata(&value as &Dyn); + let ptr = Box::into_raw_non_null(Box::new(WithVTable { vtable, value })).cast(); + ThinBox { ptr, phantom: PhantomData } + } +} +``` + +(Another possible constructor is `pub fn new_copy(value: &Dyn) where Dyn: Copy`, +but it would involve slightly more code.) + +Accessing the value requires knowing its alignment: ```rust -fn callback_from_raw_parts(data: *const (), vtable: &'static VTable) -> Box { - Box::from_raw(<*mut Fn()>::from_raw_parts(new_data, vtable)) +impl ThinBox { + fn data_ptr(&self) -> *mut () { + unsafe { + let offset = std::mem::size_of::<&'static VTable>(); + let value_align = self.ptr.as_ref().vtable.align(); + let offset = align_up_to(offset, value_align); + (self.ptr.as_ptr() as *mut u8).add(offset) as *mut () + } + } +} + +/// +fn align_up_to(offset: usize, align: usize) -> usize { + offset.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1) +} + +// Similarly Deref +impl DerefMut for ThinBox { + fn deref_mut(&mut self) -> &mut Dyn { + unsafe { + &mut *<*mut Dyn>::from_raw_parts(self.data_ptr(), *self.ptr.as_ref().vtable) + } + } } ``` -`VTable` also provides enough information to manage memory allocation: +Finally, in `Drop` we can take advantage of `Box` again, +but this time ```rust -// Equivalent to letting `Box` go out of scope to have its destructor run, -// this only demonstrates some APIs. -fn drop_callback(f: Box) { - let raw = Box::into_raw(f); - let vtable = metadata(raw); - unsafe { - vtable.drop_in_place(raw as *mut ()); - std::alloc::dealloc(raw as *mut u8, vtable.layout()); +impl Drop for ThinBox { + fn drop(&mut self) { + unsafe { + Box::::from_raw(&mut **self); + } } } ``` + # Reference-level explanation [reference-level-explanation]: #reference-level-explanation From 2edbe38199ed14559a780460462a48e257d5e247 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 31 Oct 2018 15:56:24 +0100 Subject: [PATCH 18/31] Pointer metadata: more unresolved questions --- text/0000-ptr-meta.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 4dc6dbfea70..1627845d3e9 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -392,3 +392,7 @@ Except for `VTable`’s methods, this RFC proposes a subset of what that thread * Should `VTable::drop_in_place` be removed? `vtable.drop_in_place(data_ptr)` is identical to `<*mut _>::from_raw_parts(data_ptr, vtable).drop_in_place()` + +* Should `<*mut _>::from_raw_parts` and friends be `unsafe fn`s? + +* API design: free functions v.s. methods/constructors on `*mut _` and `*const _`? \ No newline at end of file From bb5e09a21f3c8d03e20c6662668d04ac6e0ee009 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 14 Nov 2018 15:36:31 +0100 Subject: [PATCH 19/31] Pointer metadata: typo --- text/0000-ptr-meta.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 1627845d3e9..c4965684436 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -234,7 +234,7 @@ pub trait Pointee { /// Pointers to types implementing this trait alias are “thin”: /// /// ```rust -/// fn always_true() -> bool { +/// fn always_true() -> bool { /// assert_eq(std::mem::size_of::<&T>(), std::mem::size_of::()) /// } /// ``` @@ -395,4 +395,4 @@ Except for `VTable`’s methods, this RFC proposes a subset of what that thread * Should `<*mut _>::from_raw_parts` and friends be `unsafe fn`s? -* API design: free functions v.s. methods/constructors on `*mut _` and `*const _`? \ No newline at end of file +* API design: free functions v.s. methods/constructors on `*mut _` and `*const _`? From bc2a2e7322052e214daca72be8cc9fd175f3922d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 14 Nov 2018 15:36:58 +0100 Subject: [PATCH 20/31] Pointer metadata: add an `Metadata: Unpin` bound --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index c4965684436..4cd7ed0d65c 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -228,7 +228,7 @@ This similarly includes extern types. #[lang = "pointee"] pub trait Pointee { /// The type for metadata in pointers and references to `Self`. - type Metadata: Copy + Send + Sync + Ord + Hash + 'static; + type Metadata: Copy + Send + Sync + Ord + Hash + Unpin + 'static; } /// Pointers to types implementing this trait alias are “thin”: From 7be23492310a62e3fa0255eba4a3d57231f90f91 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 21 Jun 2019 07:07:04 -0700 Subject: [PATCH 21/31] Pointer metadata RFC: remove `VTable::drop_in_place` It is the same as `std::ptr::drop_in_place(<*mut _>::from_raw_parts(data, self))` --- text/0000-ptr-meta.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 4cd7ed0d65c..cb86062eb70 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -314,13 +314,6 @@ impl VTable { alloc::Layout::from_size_align_unchecked(self.size(), self.align()) } } - - /// Drops the value pointed at by `data` assuming it has the type - /// associated with this vtable. - /// - /// Behaviour is Undefined if the type isn’t correct or the pointer - /// is invalid to reinterpret as `&mut TheType`. - pub unsafe fn drop_in_place(&self, data: *mut ()) { ... } } ``` @@ -389,10 +382,6 @@ Except for `VTable`’s methods, this RFC proposes a subset of what that thread * Are there other generic standard library APIs like `ptr::null()` that have an (implicit) `T: Sized` bound that unneccesarily excludes extern types? -* Should `VTable::drop_in_place` be removed? - `vtable.drop_in_place(data_ptr)` is identical to - `<*mut _>::from_raw_parts(data_ptr, vtable).drop_in_place()` - * Should `<*mut _>::from_raw_parts` and friends be `unsafe fn`s? * API design: free functions v.s. methods/constructors on `*mut _` and `*const _`? From 538b9aa10982f72b83ae038289faa1266d2bbd6d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 21 Jun 2019 19:50:00 +0200 Subject: [PATCH 22/31] Pointer metadata RFC: replace `&'static VTable` with `DynMetadata` --- text/0000-ptr-meta.md | 61 +++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index cb86062eb70..1a304a68100 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -81,18 +81,18 @@ First, let’s get some boilerplate out of the way: ```rust use std::marker::{PhantomData, Unsize}; -use std::ptr::{self, VTable}; +use std::ptr::{self, DynMetadata}; -trait DynTrait = Pointee; +trait DynTrait = Pointee; pub struct ThinBox { - ptr: ptr::NonNull>, + ptr: ptr::NonNull>, phantom: PhantomData, } #[repr(C)] -struct WithVTable { - vtable: &'static VTable, +struct WithMeta { + vtable: DynMetadata, value: T, } ``` @@ -110,7 +110,7 @@ We let `Box` do the memory layout computation and allocation: impl ThinBox { pub fn new_unsize(value: S) -> Self where S: Unsize { let vtable = ptr::metadata(&value as &Dyn); - let ptr = Box::into_raw_non_null(Box::new(WithVTable { vtable, value })).cast(); + let ptr = Box::into_raw_non_null(Box::new(WithMeta { vtable, value })).cast(); ThinBox { ptr, phantom: PhantomData } } } @@ -125,7 +125,7 @@ Accessing the value requires knowing its alignment: impl ThinBox { fn data_ptr(&self) -> *mut () { unsafe { - let offset = std::mem::size_of::<&'static VTable>(); + let offset = std::mem::size_of::(); let value_align = self.ptr.as_ref().vtable.align(); let offset = align_up_to(offset, value_align); (self.ptr.as_ptr() as *mut u8).add(offset) as *mut () @@ -175,6 +175,7 @@ are added to `core::ptr` and re-exported in `std::ptr`: If this RFC is implemented before type aliases are, uses of `Thin` should be replaced with its definition. * A `metadata` free function +* A `DynMetadata` struct * A `from_raw_parts` constructor for each of `*const T` and `*mut T` The bounds on `null()` and `null_mut()` function in that same module @@ -206,7 +207,7 @@ This similarly includes extern types. /// a data pointer that contains the memory address of the value, and some metadata. /// /// For statically-sized types that implement the `Sized` traits, -/// pointers are said to be “thin”, metadata is zero-sized, and metadata’s type is `()`. +/// pointers are said to be “thin”: metadata is zero-sized and its type is `()`. /// /// Pointers to [dynamically-sized types][dst] are said to be “fat” /// and have non-zero-sized metadata: @@ -214,7 +215,7 @@ This similarly includes extern types. /// * For structs whose last field is a DST, metadata is the metadata for the last field /// * For the `str` type, metadata is the length in bytes as `usize` /// * For slice types like `[T]`, metadata is the length in items as `usize` -/// * For trait objects like `dyn SomeTrait`, metadata is [`&'static VTable`][VTable] +/// * For trait objects like `dyn SomeTrait`, metadata is [`DynMetadata`]. /// /// In the future, the Rust language may gain new kinds of types /// that have different pointer metadata. @@ -234,8 +235,8 @@ pub trait Pointee { /// Pointers to types implementing this trait alias are “thin”: /// /// ```rust -/// fn always_true() -> bool { -/// assert_eq(std::mem::size_of::<&T>(), std::mem::size_of::()) +/// fn this_never_panics() { +/// assert_eq!(std::mem::size_of::<&T>(), std::mem::size_of::()) /// } /// ``` pub trait Thin = Pointee; @@ -278,11 +279,12 @@ impl NonNull { } } -/// The vtable for a trait object. +/// The metadata for a `dyn SomeTrait` trait object type. /// -/// A vtable (virtual call table) represents all the necessary information +/// It is a pointer to a vtable (virtual call table) +/// that represents all the necessary information /// to manipulate the concrete type stored inside a trait object. -/// It notably it contains: +/// The vtable notably it contains: /// /// * type size /// * type alignment @@ -292,24 +294,25 @@ impl NonNull { /// Note that the first three are special because they’re necessary to allocate, drop, /// and deallocate any trait object. /// -/// The layout of vtables is still unspecified, so this type is one a more-type-safe -/// convenience for accessing those 3 special values. Note however that `VTable` does +/// The layout of vtables is still unspecified, so this type is a more-type-safe +/// convenience for accessing those 3 special values. Note however that `DynMetadata` does /// not actually know the trait it’s associated with, indicating that, at very least, /// the location of `size`, `align`, and `drop_in_place` is identical for all /// trait object vtables in a single program. -pub struct VTable { - _priv: (), +#[derive(Copy, Clone)] +pub struct DynMetadata { + vtable_ptr: &'static (), } -impl VTable { +impl DynMetadata { /// Returns the size of the type associated with this vtable. - pub fn size(&self) -> usize { ... } + pub fn size(self) -> usize { ... } /// Returns the alignment of the type associated with this vtable. - pub fn align(&self) -> usize { ... } + pub fn align(self) -> usize { ... } /// Returns the size and alignment together as a `Layout` - pub fn layout(&self) -> alloc::Layout { + pub fn layout(self) -> alloc::Layout { unsafe { alloc::Layout::from_size_align_unchecked(self.size(), self.align()) } @@ -332,14 +335,14 @@ is Undefined Behavior with a dangling data pointer. A [previous iteration][2579] of this RFC proposed a `DynTrait` that would only be implemented for trait objects like `dyn SomeTrait`. -There would be no `Metadata` associated type, `&'static VTable` was hard-coded in the trait. +There would be no `Metadata` associated type, `DynMetadata` was hard-coded in the trait. In addition to being more general and (hopefully) more compatible with future custom DSTs proposals, this RFC resolves the question of what happens if trait objects with super-fat pointers with multiple vtable pointers are ever added. -(Answer: they can use a different metadata type like `[&'static VTable; N]`.) +(Answer: they can use a different metadata type like `[DynMetadata; N]`.) -`VTable` could be made generic with a type parameter for the trait object type that it describes. +`DynMetadata` could be made generic with a type parameter for the trait object type that it describes. This would avoid forcing that the size, alignment, and destruction pointers be in the same location (offset) for every vtable. But keeping them in the same location is probaly desirable anyway to keep code size small. @@ -353,7 +356,7 @@ But keeping them in the same location is probaly desirable anyway to keep code s A previous [Custom Dynamically-Sized Types][cdst] RFC was postponed. [Internals thread #6663][6663] took the same ideas and was even more ambitious in being very general. -Except for `VTable`’s methods, this RFC proposes a subset of what that thread did. +Except for `DynMetadata`’s methods, this RFC proposes a subset of what that thread did. [cdst]: https://github.com/rust-lang/rfcs/pull/1524 [6663]: https://internals.rust-lang.org/t/pre-erfc-lets-fix-dsts/6663 @@ -364,11 +367,7 @@ Except for `VTable`’s methods, this RFC proposes a subset of what that thread * The name of `Pointee`. [Internals thread #6663][6663] used `Referent`. -* The location of `VTable`. Is another module more appropriate than `std::ptr`? - -* Should `VTable` be an extern type? - Rather than a zero-size struct? Actual vtables vary in size, - but `VTable` presumably is never used exept behind `&'static`. +* The location of `DynMetadata`. Is another module more appropriate than `std::ptr`? * The name of `Thin`. This name is short and sweet but `T: Thin` suggests that `T` itself is thin, From e47aa6e397e32fd5c43eb8a2b946db831b17c473 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 22 Jun 2019 15:19:53 +0200 Subject: [PATCH 23/31] Pointer metadata RFC: mention extern types in doc-comment. --- text/0000-ptr-meta.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 1a304a68100..a70c35f3ce0 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -206,7 +206,8 @@ This similarly includes extern types. /// Raw pointer types and reference types in Rust can be thought of as made of two parts: /// a data pointer that contains the memory address of the value, and some metadata. /// -/// For statically-sized types that implement the `Sized` traits, +/// For statically-sized types (that implement the `Sized` traits) +/// as well as for `extern` types, /// pointers are said to be “thin”: metadata is zero-sized and its type is `()`. /// /// Pointers to [dynamically-sized types][dst] are said to be “fat” From 29c754710638cbf6236b8a7c404e2d9fd8c49101 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 14 Sep 2020 00:29:28 +0200 Subject: [PATCH 24/31] Pointer metadata RFC: use `NonNull` instead of `&'static` for vtable pointers In response to https://github.com/rust-lang/rfcs/pull/2580#discussion_r486505016 --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index a70c35f3ce0..3e71faa42b4 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -302,7 +302,7 @@ impl NonNull { /// trait object vtables in a single program. #[derive(Copy, Clone)] pub struct DynMetadata { - vtable_ptr: &'static (), + vtable_ptr: ptr::NonNull<()>, } impl DynMetadata { From 1e5622f01d2912a687f7761afc9f3b0c45745419 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 14 Sep 2020 00:34:50 +0200 Subject: [PATCH 25/31] Pointer metadata RFC: add unresolved question for parameterizing `DynMetadata` --- text/0000-ptr-meta.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 3e71faa42b4..62fb467cd94 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -370,6 +370,12 @@ Except for `DynMetadata`’s methods, this RFC proposes a subset of what that th * The location of `DynMetadata`. Is another module more appropriate than `std::ptr`? +* Should `DynMetadata` have a type parameter for what trait object type it is a metadata of? + Such that `::Metadata` is `DynMetadata`. + This would allow the memory layout to change based on the trait object type + (potentially super-wide pointers with multiple vtable pointers for multi-trait objects?) + or to have different methods. + * The name of `Thin`. This name is short and sweet but `T: Thin` suggests that `T` itself is thin, rather than pointers and references to `T`. From 04ba25fbca87657077653f52b4b051396996c749 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 14 Sep 2020 08:50:45 +0200 Subject: [PATCH 26/31] Pointer metadata RFC: the Drop impl example written two years ago appears broken --- text/0000-ptr-meta.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 62fb467cd94..9ef86eb9589 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -110,7 +110,7 @@ We let `Box` do the memory layout computation and allocation: impl ThinBox { pub fn new_unsize(value: S) -> Self where S: Unsize { let vtable = ptr::metadata(&value as &Dyn); - let ptr = Box::into_raw_non_null(Box::new(WithMeta { vtable, value })).cast(); + let ptr = NonNull::from(Box::leak(Box::new(WithMeta { vtable, value }))).cast(); ThinBox { ptr, phantom: PhantomData } } } @@ -148,14 +148,16 @@ impl DerefMut for ThinBox { } ``` -Finally, in `Drop` we can take advantage of `Box` again, -but this time +Finally, in `Drop` we may not be able to take advantage of `Box` again +since the original `Sized` type `S` is not statically known at this point. ```rust impl Drop for ThinBox { fn drop(&mut self) { unsafe { - Box::::from_raw(&mut **self); + let layout = /* left as an exercise for the reader */; + ptr::drop_in_place::(&mut **self); + alloc::dealloc(self.ptr.cast(), layout); } } } @@ -176,7 +178,7 @@ are added to `core::ptr` and re-exported in `std::ptr`: uses of `Thin` should be replaced with its definition. * A `metadata` free function * A `DynMetadata` struct -* A `from_raw_parts` constructor for each of `*const T` and `*mut T` +* A `from_raw_parts` constructor for each of `*const T`, `*mut T`, and `NonNull`. The bounds on `null()` and `null_mut()` function in that same module as well as the `NonNull::dangling` constructor From f94f627211d62130c99878831f824b4ab3783754 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 29 Dec 2020 15:49:56 +0100 Subject: [PATCH 27/31] Pointer metadata: remove 'static bound on the Metadata associated type As requested in https://github.com/rust-lang/rfcs/pull/2580#issuecomment-722398628 --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 9ef86eb9589..27c4bf20b91 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -232,7 +232,7 @@ This similarly includes extern types. #[lang = "pointee"] pub trait Pointee { /// The type for metadata in pointers and references to `Self`. - type Metadata: Copy + Send + Sync + Ord + Hash + Unpin + 'static; + type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; } /// Pointers to types implementing this trait alias are “thin”: From 236e65745f35e19f542bc35e596b00eed4ef3bbb Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 29 Dec 2020 15:54:26 +0100 Subject: [PATCH 28/31] Pointer metadata: add unresolved question for `into_raw_parts` --- text/0000-ptr-meta.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 27c4bf20b91..d62b8ae392b 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -393,3 +393,8 @@ Except for `DynMetadata`’s methods, this RFC proposes a subset of what that th * Should `<*mut _>::from_raw_parts` and friends be `unsafe fn`s? * API design: free functions v.s. methods/constructors on `*mut _` and `*const _`? + +* Add `into_raw_parts` that returns `(*const (), T::Metadata)`? + Using the `cast` method to a `Sized` type to extract the address as a thin pointer + is less discoverable. + Possibly *instead* of the metadata function? From 7b0175e688532d9496874f6b393b8aafc05a538a Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 29 Dec 2020 16:37:38 +0100 Subject: [PATCH 29/31] Pointer metadata: parameterize `DynMetadata` over its `dyn Trait` type --- text/0000-ptr-meta.md | 47 ++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index d62b8ae392b..89804de8e0d 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -83,9 +83,9 @@ First, let’s get some boilerplate out of the way: use std::marker::{PhantomData, Unsize}; use std::ptr::{self, DynMetadata}; -trait DynTrait = Pointee; +trait DynTrait = Pointee>; -pub struct ThinBox { +pub struct ThinBox> { ptr: ptr::NonNull>, phantom: PhantomData, } @@ -125,7 +125,7 @@ Accessing the value requires knowing its alignment: impl ThinBox { fn data_ptr(&self) -> *mut () { unsafe { - let offset = std::mem::size_of::(); + let offset = std::mem::size_of::(); let value_align = self.ptr.as_ref().vtable.align(); let offset = align_up_to(offset, value_align); (self.ptr.as_ptr() as *mut u8).add(offset) as *mut () @@ -218,15 +218,17 @@ This similarly includes extern types. /// * For structs whose last field is a DST, metadata is the metadata for the last field /// * For the `str` type, metadata is the length in bytes as `usize` /// * For slice types like `[T]`, metadata is the length in items as `usize` -/// * For trait objects like `dyn SomeTrait`, metadata is [`DynMetadata`]. +/// * For trait objects like `dyn SomeTrait`, metadata is [`DynMetadata`][DynMetadata] +/// (e.g. `DynMetadata`). /// /// In the future, the Rust language may gain new kinds of types /// that have different pointer metadata. /// /// Pointer metadata can be extracted from a pointer or reference with the [`metadata`] function. /// The data pointer can be extracted by casting a (fat) pointer -/// to a (thin) pointer to a `Sized` type the `as` operator, -/// for example `(x: &dyn SomeTrait) as *const SomeTrait as *const ()`. +/// to a (thin) pointer to a `Sized` type with the `as` operator, +/// for example `(x: &dyn SomeTrait) as *const SomeTrait as *const ()` +/// or `(x: *const dyn SomeTrait).cast::<()>()`. /// /// [dst]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts #[lang = "pointee"] @@ -282,7 +284,7 @@ impl NonNull { } } -/// The metadata for a `dyn SomeTrait` trait object type. +/// The metadata for a `DynTrait = dyn SomeTrait` trait object type. /// /// It is a pointer to a vtable (virtual call table) /// that represents all the necessary information @@ -297,17 +299,16 @@ impl NonNull { /// Note that the first three are special because they’re necessary to allocate, drop, /// and deallocate any trait object. /// -/// The layout of vtables is still unspecified, so this type is a more-type-safe -/// convenience for accessing those 3 special values. Note however that `DynMetadata` does -/// not actually know the trait it’s associated with, indicating that, at very least, -/// the location of `size`, `align`, and `drop_in_place` is identical for all -/// trait object vtables in a single program. +/// It is possible to name this struct with a type parameter that is not a `dyn` trait object +/// (for example `DynMetadata`) but not to obtain a meaningful value of that struct. #[derive(Copy, Clone)] -pub struct DynMetadata { +pub struct DynMetadata { + // Private fields vtable_ptr: ptr::NonNull<()>, + phantom: PhantomData } -impl DynMetadata { +impl DynMetadata { /// Returns the size of the type associated with this vtable. pub fn size(self) -> usize { ... } @@ -343,12 +344,8 @@ In addition to being more general and (hopefully) more compatible with future custom DSTs proposals, this RFC resolves the question of what happens if trait objects with super-fat pointers with multiple vtable pointers are ever added. -(Answer: they can use a different metadata type like `[DynMetadata; N]`.) - -`DynMetadata` could be made generic with a type parameter for the trait object type that it describes. -This would avoid forcing that the size, alignment, and destruction pointers -be in the same location (offset) for every vtable. -But keeping them in the same location is probaly desirable anyway to keep code size small. +(Answer: they can use a different metadata type, +possibly like `(DynMetadata, DynMetadata)`.) [2579]: https://github.com/rust-lang/rfcs/pull/2579 @@ -372,11 +369,11 @@ Except for `DynMetadata`’s methods, this RFC proposes a subset of what that th * The location of `DynMetadata`. Is another module more appropriate than `std::ptr`? -* Should `DynMetadata` have a type parameter for what trait object type it is a metadata of? - Such that `::Metadata` is `DynMetadata`. - This would allow the memory layout to change based on the trait object type - (potentially super-wide pointers with multiple vtable pointers for multi-trait objects?) - or to have different methods. +* Should `DynMetadata` not have a type parameter? + This might reduce monomorphization cost, + but would force that the size, alignment, and destruction pointers + be in the same location (offset) for every vtable. + But keeping them in the same location is probaly desirable anyway to keep code size small. * The name of `Thin`. This name is short and sweet but `T: Thin` suggests that `T` itself is thin, From 3ff39f09ba6a202fc9c6c26ef1202243744b8fdb Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 23 Jan 2021 09:14:22 +0100 Subject: [PATCH 30/31] Typo fix Co-authored-by: yvt --- text/0000-ptr-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ptr-meta.md b/text/0000-ptr-meta.md index 89804de8e0d..1405c9a0caf 100644 --- a/text/0000-ptr-meta.md +++ b/text/0000-ptr-meta.md @@ -203,7 +203,7 @@ This similarly includes extern types. [extern types]: https://github.com/rust-lang/rust/issues/43467 ```rust -/// This trait is automatically implement for every type. +/// This trait is automatically implemented for every type. /// /// Raw pointer types and reference types in Rust can be thought of as made of two parts: /// a data pointer that contains the memory address of the value, and some metadata. From 50b567b607cd3774f55e520c2cc08a515673376a Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 29 Jan 2021 22:43:40 +1000 Subject: [PATCH 31/31] RFC 2580: Pointer metadata --- text/{0000-ptr-meta.md => 2580-ptr-meta.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{0000-ptr-meta.md => 2580-ptr-meta.md} (99%) diff --git a/text/0000-ptr-meta.md b/text/2580-ptr-meta.md similarity index 99% rename from text/0000-ptr-meta.md rename to text/2580-ptr-meta.md index 1405c9a0caf..4da58d8fba5 100644 --- a/text/0000-ptr-meta.md +++ b/text/2580-ptr-meta.md @@ -1,7 +1,7 @@ - Feature Name: `ptr-meta` - Start Date: 2018-10-26 -- RFC PR: -- Rust Issue: +- RFC PR: https://github.com/rust-lang/rfcs/pull/2580 +- Rust Issue: https://github.com/rust-lang/rust/issues/81513 # Summary [summary]: #summary