Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRFC: Pointer metadata & VTable #2580
Conversation
SimonSapin
added some commits
Oct 25, 2018
This comment has been minimized.
This comment has been minimized.
|
A previous iteration of this RFC is also visible at #2579. It was based on @Gankro’s proposal https://gist.github.com/Gankro/b053cb4d1cb3bcaec070de89734720f7 |
kennytm
reviewed
Oct 27, 2018
text/0000-ptr-meta.md Outdated
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Oct 27, 2018
•
|
First of all, thanks for opening this rfc. It's the right way to fix the raw::TraitObject API, and is a big step toward custom DST. My only criticism is I think the Vtable type should be generic over the trait object type, as mentioned in the alternatives section. Having different I like the name |
This comment has been minimized.
This comment has been minimized.
|
cc @ubsan |
oli-obk
reviewed
Oct 27, 2018
text/0000-ptr-meta.md Outdated
text/0000-ptr-meta.md Outdated
text/0000-ptr-meta.md Outdated
text/0000-ptr-meta.md Outdated
text/0000-ptr-meta.md Outdated
text/0000-ptr-meta.md Outdated
| /// | ||
| /// [dst]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts | ||
| #[lang = "pointee"] | ||
| pub trait Pointee { |
This comment has been minimized.
This comment has been minimized.
oli-obk
Oct 27, 2018
Contributor
so... I'm assuming the compiler implements
default impl<T: ?Sized> Pointee for T {
type Metadata = &'static Vtable;
}
impl<T: Sized> Pointee for T {
type Metadata = ();
}
impl Pointee for str {
type Metadata = usize;
}
impl<T: Sized> Pointee for [T] {
type Metadata = usize;
}Which means theoretically we could make Vtable generic over T allowing the drop_in_place method to take a raw pointer with the correct pointee type?
This comment has been minimized.
This comment has been minimized.
SimonSapin
Oct 28, 2018
Author
Contributor
These impls would be accurate in current Rust, but what I had in mind instead was that the compiler would automatically generate impls, similar to what it does for the std::marker::Unsize trait. As far as the standard library is concerned these impls would be "magic", not based on specialization.
Regardless, yes, making VTable generic with a type parameter for the trait object type is possible.
text/0000-ptr-meta.md Outdated
| (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 |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
SimonSapin
Oct 28, 2018
Author
Contributor
Without a type parameter, x.size() with x: &'static VTable necessarily executes the same code for any vtable. With a type parameter, x: &'static VTable<dyn Foo> and x: &'static VTable<dyn Bar> are different types and could execute different code. (For example, do table lookup with different offsets.) However, keeping the offset of size the same within all vtables might be desirable regardless of this API.
scottmcm
reviewed
Oct 28, 2018
| `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 |
This comment has been minimized.
This comment has been minimized.
| type Metadata; | ||
| } | ||
| /// Pointers to types implementing this trait alias are |
This comment has been minimized.
This comment has been minimized.
scottmcm
added
T-lang
T-libs
labels
Oct 28, 2018
This comment has been minimized.
This comment has been minimized.
|
@mikeyhew I’ve very open to adding a type parameter to As to supporting super-fat pointers with multiple vtable pointers, as mentioned in the alternatives section I believe this design doesn’t prevent it. Types that don’t exist yet and are added to the language in the future (possibly custom DSTs) can have a different metadata type. For |
Centril
reviewed
Oct 28, 2018
| pub unsafe fn drop_in_place(&self, data: *mut ()) { ... } | ||
| } | ||
| ``` | ||
|
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
SimonSapin
Oct 28, 2018
Author
Contributor
I came up short trying to think of a reason not to do this at all (as opposed to doing it differently). Suggestions welcome.
| 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]`.) |
This comment has been minimized.
This comment has been minimized.
Centril
Oct 28, 2018
Contributor
Should then [&'static VTable; 1] for dyn SomeTrait be used to make that transition smoother and to fit better with const generics?
This comment has been minimized.
This comment has been minimized.
SimonSapin
Oct 28, 2018
Author
Contributor
This would make some sense if we were definitely gonna have super-fat pointers with multiple separate vtable pointers as fat pointer metadata. But if we don’t and end up with a different solution to upcasting, we’ll end up with a always-single-item arrays for no reason. This isn’t really the thread to get into that discussion, but my opinion is that super-fat pointer have a significant enough size cost that I’d much prefer a different solution.
Perhaps an alternative for this RFC, more neutral with respect super-fat pointers v.s. not, would be to have type Metadata = VTable<Self>; for trait objects. (See other comments about VTable’s possible type paramater.) With the pointer/reference indirection hidden away in private fields of the VTable type, this design would be compatible with having VTable<dyn A + B> contain two pointers in the future.
This comment has been minimized.
This comment has been minimized.
SimonSapin
Oct 28, 2018
Author
Contributor
Also, is there a use case for generic code that accepts any trait object with any number of vtable pointer but not other kinds of DSTs?
| 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]`.) |
This comment has been minimized.
This comment has been minimized.
Centril
Oct 28, 2018
Contributor
Are we doing the proposals in the right order? Shouldn't we focus on dealing with dyn A + B + C, upcasting, and such things first? Also, cc #2035.
This comment has been minimized.
This comment has been minimized.
SimonSapin
Oct 28, 2018
Author
Contributor
I believe the design proposed here is compatible enough with various options for multi-traits trait objects and upcasting such that there isn’t a strong dependency, and we don’t need to block this RFC on everything else being settled.
|
|
||
| * The name of `Pointee`. [Internals thread #6663][6663] used `Referent`. | ||
|
|
||
| * The location of `VTable`. Is another module more appropriate than `std::ptr`? |
This comment has been minimized.
This comment has been minimized.
Centril
Oct 28, 2018
Contributor
and should it be called Dictionary instead? ("type class dictionary")
This comment has been minimized.
This comment has been minimized.
kennytm
Oct 28, 2018
Member
Big -1 to calling it Dictionary since this typically means a key-value map (example: C#, Swift, Python).
Furthermore, here in Rust the VTable is implemented as an array of function pointers, not a HashMap (unlike e.g. Python where it is really implemented as a dict), so calling it Dictionary obscures the alleged complexity.
This comment has been minimized.
This comment has been minimized.
SimonSapin
Oct 28, 2018
Author
Contributor
Even if the implementation happened to use HashMap, I’d prefer VTable since it’s more descriptive of the role of this type. (As opposed to: dictionary of what?) I believe that vtable is a well-enough established term of art.
kennytm
and others
added some commits
Oct 28, 2018
This comment has been minimized.
This comment has been minimized.
|
Regarding making struct VTable<Dyn> { … }… then it could be used with any type as a parameter. What does struct VTable<Dyn> where Dyn: ?Sized + std::marker::DynTrait { … }Do we want such a trait? |
This comment has been minimized.
This comment has been minimized.
|
Hmm, maybe we could get away with this? struct VTable<Dyn> where Dyn: ?Sized + Pointee<Metadata=Self> { … } |
This comment has been minimized.
This comment has been minimized.
|
Is there any way to ensure minimal compiler time is wasted performing monomorphization on useless vtable type params? If not, I would rather the API just be less safe. |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Oct 28, 2018
|
@Gankro the word "useless" sounds a little strong there... the purpose it to make sure you can't use a vtable from a |
This comment has been minimized.
This comment has been minimized.
|
You're supposing a situation where I somehow am writing code with two vtable types floating around, in which case I have two data pointers, and nothing can stop me from swapping the data pointers, producing the exact same effect. |
SimonSapin
referenced this pull request
Oct 28, 2018
Open
Tracking issue for `raw` stabilization (`raw::TraitObject`) #27751
This comment has been minimized.
This comment has been minimized.
|
|
Centril
added
A-traits
A-traits-libstd
A-types-libstd
A-repr
A-trait-object
labels
Nov 22, 2018
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Nov 25, 2018
|
Would it be OK if we changed the struct DynTraitMeta<T: ?Sized> {
vtable: &'static Vtable,
// not exactly sure what to put here, but this works for now. It must contain `T`.
_marker: PhantomData<*const T>,
}This type could be kept unstable, so that it could be changed in the future (potentially to I'm uneasy about using |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Nov 25, 2018
|
Here is one more argument for having different metadata types for different
If Now someone might say that "from_raw_parts" is an unsafe-sounding API that you would never expect to be safe. But I think that's just because of the name. It comes from We have an opportunity to make something that sounds unsafe — messing around with pointer metadata and vtables — actually be a completely safe thing to do. I think that's pretty cool, and it's a great example of the kind of programming Rust lets you do. And there are no downsides here. The only one that was suggested — potentially extra monomorphization because of an extra type parameter — doesn't hold any water. And even if it did, it seems like something that would come up so rarely that it wouldn't matter, or that could be fixed by future optimizations. |
This comment has been minimized.
This comment has been minimized.
Extending data invariants like this has repercussions beyond just this RFC. For example, it can make unsafe code invalid even if it doesn't interact with the abstractions introduced here, and if we take this step we should do due digilence to check that doesn't affect any unsafe code we want to allow. Not necessarily a problem, but it's extra work that needs to be motivated.
I don't understand how anything substantial can be made safe this way. The data pointer part of a raw pointer can be invalid, so even if one defines a method with raw pointer receiver type and can call it that way, that method couldn't really do anything with the trait object (if it's safe). While there are some bits and pieces of information one might want to put into a trait that are solely about the type it's implemented on rather than a specific object,
This function is trivial and all the uses I know of are embedded into a context where its use is crucial to other, far more subtle, unsafe code. For example, making this function safe does not help at all when converting the If not, I do not see any reason why this would make anything safer. As a general principle, usage of nominally-safe functions deeply embedded in unsafe code is often critical for the soundness of the whole code, so the advantage of making more functionality safe lies in enabling entirely safe code, not in being able to move one important line out of the
Sorry, just asserting that is not convincing. That type parameters lead to monomorphization today is a fact. That monomorphization generally causes binary size issues is also well established. That only a neglegible amount of code would be redundantly monomorphized is not obvious to me -- there are entire families of data structures that could be built on this abstraction, not to mention all the generic utilities that may be used while working on those vtable pointers. Appeals to future optimizations / "sufficiently smart compilers" also aren't great, as the extent and efficancy of these optimizations is still somewhat unknown (as they aren't implemented and we also don't yet know all the details of the code we'd want to optimize). Not to mention that even if the overhead would eventually become zero, it's still not great to have the overhead for the medium-term future. |
This comment has been minimized.
This comment has been minimized.
|
The question of whether metadata value in a raw pointer should always be valid is completely independent of whether the metadata type for
|
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Nov 26, 2018
Actually, it is. (Sort of.) If a |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Nov 26, 2018
Fair point. I'm with you on that.
OK. Well I'm not sure what it will take to convince you, but I'll try. Like I said earlier in this thread, all those data structures and generic utilities will already be generic on the pointee type. Or they might be specific to one trait object type. This one extra struct having the same type parameter that everything else does won't result in any extra functions being monomorphized in either of those cases, because either the code would be monomorphized anyway (in the case of code that is generic over the pointee type), or it would not be monomorphized even with the type parameter (in the case of code that is specific to one If you're still not convinced, give me an example of code that you would want to write that you think would not be monomorphized if the metadata type didn't have the type parameter, and would be monomorphized if it did. |
This comment has been minimized.
This comment has been minimized.
Even the exampled sketched in the RFC, Specifically, let's look at If the type of the vtable is parametrized by the trait object, one can still extract the arithmetic into monomorphic code, but it's less natural (need to extract size+align and pass them separately) and the polymorphic code contains some more instructions which are then replicated for all trait object types. |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Nov 30, 2018
|
@rkruppe thanks for replying with an example. I'm super busy right now but I'll try to read through it and get back to you next week |
This was referenced Dec 14, 2018
todo
bot
referenced this pull request
Dec 22, 2018
Open
Replace FatPtr with a libcore type when one lands. #103
This comment has been minimized.
This comment has been minimized.
|
Great RFC @SimonSapin. I really like the idea of starting with top most super trait ( |
SimonSapin
referenced this pull request
Mar 6, 2019
Open
Tracking issue for RFC 1861: Extern types #43467
nikomatsakis
added
the
I-nominated
label
Mar 8, 2019
This comment has been minimized.
This comment has been minimized.
|
Nominating for @rust-lang/lang feedback at @SimonSapin's request. I've not had time to dig into this myself. But it also seems like something that could be folded into the @rust-lang/wg-unsafe-code-guidelines's ambit. |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Mar 8, 2019
|
Sorry for taking so long to get back to this! @rkruppe you do make some good points, I guess it's more of a grey area than I thought. I think at this point it would be good to put it down as an unresolved question whether the metadata type for Let me know what you think about that. |
This comment has been minimized.
This comment has been minimized.
|
I think it doesn’t make sense to keep a type unstable if stable code can manipulate values of that type. |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Mar 9, 2019
|
@SimonSapin no VTable would still be unstable, I just meant we should make it available (but unstable) so that people can experiment with it and see if it helps reduce monomorphization. |
This comment has been minimized.
This comment has been minimized.
|
If Keeping everything from a new RFC unstable for a while so that people can experiment is the normal process. |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Mar 11, 2019
|
@SimonSapin I had the idea that in some distant future, we could let people create |
This comment has been minimized.
This comment has been minimized.
|
We discussed this RFC in our @rust-lang/lang meeting yesterday. We had a bit of trouble deciding what to do about it. The problem is not so much in the content of the RFC as it is in the process questions -- specifically, we are trying to adopt a more directed structure, where we don't start a bunch of things and then leave them half-done, but instead only start things when we are dedicated to seeing them through. One key part of that is creating working groups around the tasks we are highlighting, and trying to coordinate with e.g. the compiler team for implementation work etc. This RFC did not obviously align with any of our existing working groups or roadmap goals. Admittedly, those need to be more crisply laid out! I had hoped to point you at the lang-team repository to find a good listing of those things, but I realize now that content isn't really there. I guess the best source for it is my blog post. One thing we have talked about but clearly haven't quite settled is what to do with existing RFCs, and how to leave room for "spontaneity" -- like this RFC -- while still respecting our overall bandwidth and priorities. Anyway, all of this preamble is to say, one thing that would help us -- and forgive me if it is present on thread -- is to try to place this RFC in the context of bigger goals. For example:
I think ultimately the options on the table are:
I'm going to leave this nominated so that we check again on the answers to these questions in the next meeting. |
This comment has been minimized.
This comment has been minimized.
As to next steps, it sounds like this is more about the lang team deciding what process they want to have, rather than about this RFC specifically. |
SimonSapin commentedOct 27, 2018
Add generic APIs that allow manipulating the metadata of fat pointers:
This RFC does not propose a mechanism for defining custom dynamically-sized types, but tries to stay compatible with future proposals that do.
HTML view