-
Notifications
You must be signed in to change notification settings - Fork 214
Add KindaSortaDangling internal type to make Yoke safe under strong protection #3735
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Annoyingly this increases the sizes of some DataPayloads, presumably because the inner data is no longer eligible for niche optimizations. Though I can't really see the opportunities for niche optimizations anyway so I'm not fully clear what's going on. However these sizes are less than the size in nightly anyway, and the nightly sizes don't change with this; I'm inclined to assume that what happened here is that the MaybeUninit is triggering whatever layout optimization Rust is now applying globally, just earlier. |
check_size_of!(6792 | 5456, DateTimeFormatter); | ||
check_size_of!(7904 | 6480, ZonedDateTimeFormatter); | ||
check_size_of!(1496 | 1320, TimeFormatter); | ||
check_size_of!(5800 | 4632, DateFormatter); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I bet the compiler was previously able to "merge" the T
with whatever other fields were present on the struct, but now with the MaybeUninit it is forced to keep the payload as its own chunk of memory. A bit sad.
EDIT: Nevermind, that doesn't make sense, because it's still possible to get a &T
, so the compiler doesn't have the liberty to fiddle with the layout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah it only makes sense if there's an enum wrapping it, and the only enum I see is the thing that swaps between yokes and staticrefs, and I'm not quite sure how that could have a size impact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like if the payload of two variants have a common part, and if one of the payloads separately has enough of a niche for Rust to be able to stuff the enum
's discriminant inside, then it does so, effectively reducing the size of the whole enum
. In which case the niche removal of MaybeUninit
does effectively impact the overall size.
use ::core::mem::{MaybeUninit as MU, size_of};
enum Enum<T> {
A(T, u8),
B( u8),
}
assert_eq!(2, size_of::<Enum< bool >>());
assert_eq!(3, size_of::<Enum< MU<bool> >>());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aha! thanks for investigating!
Though the DataPayload
enum here doesn't have variants with common parts before it gets to the yoke
I kinda prefer using the crate |
I really don't think this type is specific or tricky: It's just a wrapped The main tricky thing I see here is whether or not this type has the effect we want it to have, which definitely is based on some tricky stuff. However, we are acting on the direct advice of the unsafe code guidelines group; I'm fine doing that. |
What I mean is that I'm cross-referencing this with the comments in #3696 and the implementation in the Also, it seems |
There's a lot going in that crate for primarily two reasons: Firstly, it also attempts to expose a retag-safe The crate is also planning to build.rs a compat strategy as the right types become available; personally I'm fine with upgrading once the RFC lands and MSRV lets us because this stuff is not currently exploited by the compiler, is unlikely to be, and "miri clean on older compiler" does not seem like a goal we currently plan to hit given that "miri clean on a modern compiler" is not something we're doing across ICU4X 😄. Secondly, it attempts to handle dropck issues. These show up when your parameters have lifetimes because Rust does let you make things like self-referencing arenas where the self-references have lifetimes are not allowed to have destructors that can observe the deletion-in-progress data (here's an example that may be illustrative, happy to expand if you're interested). We don't need to do this, our yokeables are always There are also a bunch of impls we don't need because we're not exposing it to users and Yoke will want to carefully manage its own impls. I was intending to write Also, omitting It is weird from a "what it does" perspective but that's not in the actual
It is not possible for
The main difference between the two is that |
Ironically, the I am happy to try and document the situation with more detail if that helps make this code more maintainable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! (I love the name 😄) I'd just be careful with the current non-in-place drop of T
check_size_of!(6792 | 5456, DateTimeFormatter); | ||
check_size_of!(7904 | 6480, ZonedDateTimeFormatter); | ||
check_size_of!(1496 | 1320, TimeFormatter); | ||
check_size_of!(5800 | 4632, DateFormatter); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like if the payload of two variants have a common part, and if one of the payloads separately has enough of a niche for Rust to be able to stuff the enum
's discriminant inside, then it does so, effectively reducing the size of the whole enum
. In which case the niche removal of MaybeUninit
does effectively impact the overall size.
use ::core::mem::{MaybeUninit as MU, size_of};
enum Enum<T> {
A(T, u8),
B( u8),
}
assert_eq!(2, size_of::<Enum< bool >>());
assert_eq!(3, size_of::<Enum< MU<bool> >>());
Co-authored-by: Ralf Jung <[email protected]>
Co-authored-by: Ralf Jung <[email protected]>
Miri is going to be turning on
-Zmiri-retag-fields
which means that certain memory pointery things need to have the appropriate internal metadata.rust-lang/rfcs#3336 proposes a
MaybeDangling
type that allows one to mark memory regions as not needing that metadata. It does not exist yet, in the meantime I have defined an internal, simpler variant of it that achieves the task adequately.See #3696