-
Notifications
You must be signed in to change notification settings - Fork 58
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
Should we / can we make MaybeUninit<T> always preserve all bytes of T? #518
Comments
I'll add that a typed copy of an uninitialized variable is UB in C, so there's no need to promise any ABI for FFI compatibility, |
In C I think this is not true for char types. But yeah for most types you cannot pass them uninit by value.
|
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
The PR that introduced the guarantees does not talk about padding, and it seems like that wasn't really understood back then. The t-lang minutes discussing this are lost to time and reorganizations, but it seems doubtful that such a consideration was raised. Discussions from 2018 raise a lack of real-world use cases for ABI compatibility, and I agree with such a sentiment in the present. I don't this this would be approved nowadays, but I am incredibly apprehensive about removing it. There are few places in the Rust documentation that use always for guarantees like this, and the use cases for some weird FFI thunks or bindings would be nigh-impossible to properly test with crater or similar... |
This comment was marked as off-topic.
This comment was marked as off-topic.
@carbotaniuman I think we should consider removing it. If we can't come up with any legitimate usecase, I think we should definitely remove it. I don't like going back on a promise like this, but if we don't have a usecase that could be broken by taking back this promise, then the chances that someone is affected should be very slim. @Diggsey thanks for explaining why you think this belongs in this thread. But I disagree. "MaybeUninit preserves provenance" is not relevant here. You will note that provenance does not appear in the issue description. Furthermore, provenance on CHERI works like it does everywhere else, so even if provenance were relevant, CHERI wouldn't change anything. It is true that you can write code with |
If we do agree to remove the guarantee, I expect it to break 0 uses in practice. My only other concern would be the performance impact of having to copy more bytes. It probably won't affect SIMD or buffers though, so I don't really think that's it's really an issue. |
I think we should preserve the memory layout compatibility, but drop the calling convention compatibility. That could be done using |
FTR, I use However, for compatibility with gcc/clang, they have to expose an ABI equal to the rountines using primitives. |
(And in general, I agree with @carbotaniuman - unless crater is testing all kinds of targets, I'm betting it primarily tests x86_64, where aggregate-of-one-field will get passed the same way as that one field*, so without using miri-crater, the ABI checks won't be found by crater. If the code is used on something like arm32 though, it's going to be very visibility broken) |
Yes, this is super hard to test for. I wonder if it's worth having a blog post asking people whether they need this guarantee...
Yes, concretely the proposal would be:
Or maybe "no padding" should be restricted a bit further, like "if |
Is the only motivation to backing up on that promise is the fact that this is a frequent source of confusion? Which benefits except clarity can Rust gain? |
We never intended |
I'm pretty sure this is still the case, but it might be worth it to enumerate things that ARE still allowed for this wrt FFI/ABI concerns. My primary use of |
Yes, ABI compatibility is about the "by-value" part of a function argument or return type. That's how we've consistently been using this term for a while now, also see our glossary and the documentation on ABI compatibility. In public communication we'll obviously spell out the details more than in internal discussion. ("Internal" not as in "private" but as in "among the team members and anyone else who's willing to participate".) |
I at the very least need target simd types as well - for floating-point types that aren't directly supported by rust (e.g. |
Since that is a compiler-internal concern, you could also do this by providing more ABI guarantees than what Rust provides in general. But that case would be covered by "types without padding", or we could explicitly mention the stdarch SIMD types (since they are all powers of 2). |
Not fully - you don't necessarily need to compile the rtlibs with lccc themselves, they're written in mostly portable rust, and quite deliberately. I'd like to be able to continue providing that guarantee.
You can also now see a formalization in reference#1545, as a note. |
It is a frequent source of confusion that
MaybeUninit<T>
is not just preserving all the underlying bytes of storage, but actually ifT
has padding then those bytes are lost on copies/moves ofMaybeUninit<T>
.This is currently pretty much a necessary consequence of the promise that
MaybeUninit<T>
is ABI-compatible withT
: some ABIs don't preserve the padding ofT
when it is passed to a function. However, this was not part of the intention withMaybeUninit
at all, it is something we discovered later.Maybe we should try to take this back, and make the guarantee only for types without padding?
I am not even sure why we made this a guarantee. We made the type
repr(transparent)
because for performance it is quite important thatMaybeUninit<$int>
becomes just aniN
in LLVM. But that doesn't require a stable guarantee. And in fact it seems like it would almost always be a bug if the caller and callee disagree about whether the value has to be initialized. So I would be curious about real-world examples where this guarantee is needed.The text was updated successfully, but these errors were encountered: