-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Uninhabited types are ZSTs despite partial initialization being allowed in safe code. #49298
Comments
Seems I-unsound. enum Void {}
fn main() {
let mut x: (Void, [u8; 20]);
x.1 = [0x12; 20];
} |
Also, this regression was introduced in Rust 1.24.0 from what I see, this correctly works in 1.23.0. |
To pile on the scary labels, that also makes this a regression from stable to stable! |
#48493 looks like an instance of this - cc @alexcrichton |
@eddyb |
@rkruppe is there a separate bug for that? |
There's potentially an even more problematic implication here: initializing an uninhabited (because of its fields) variant and panicking while doing so could also result in writing over data following the whole let x: Option<(String, !)>;
let y = vec!["foo".to_string()];
x = Some((String::new("bar"), panic!("{:?}", y))); This might be hard to demonstrate today, because of some extra temporaries, but optimizations would likely prefer to write to |
We discussed in the meeting. It seems like the rules want to be:
This ensures there is space for non-ZST data... we need to do this if we want to be able to optimize temporaries away pre-monomorphization. |
Specifically, the data inside the variant (not including any tag or anything else enum-specific). |
Shall there be some Related idea: |
@vi Note that, IIRC, Note that #49298 (comment) is more general than partial initialization, and it concerns writes to return destination, followed by a panic later, in the context of wanting to optimize away copies. |
|
Ah, right, I was thinking of a subtler distinction, nevermind. |
fix some issues around ZST handling This fixes two bugs: - We used to entirely skip enum variants like `B([u16; 0], !)`, even failing to properly align the enum! Honoring the alignment of uninhabited variants is important for the same reason that we must reserve space for their fields -- see [here](rust-lang#49298 (comment)) for why. - ~~We uses to reject `repr(transparent)` on `struct MyType([u16; 0])`, which is weird because a one-field struct should always be allowed to be transparent around that field.~~ (moved to separate PR) I also found two places in the layout code that did something special for ZST without explaining why, and removing those special cases doesn't seem to have any effect except for reordering some zero-sized fields which shouldn't be an issue... maybe PR CI will explain why those cases were needed, or maybe they became obsolete at some point.
When we optimized product types with uninhabited fields to be ZST, partial initialization was overlooked, i.e. writes to the inhabited sibling fields are allowed despite missing in the layout.
Thankfully, it's impossible to read or borrow such fields, because the whole value must be initialized (although this may be relaxed in the future). However , the initialized fields are still dropped.
One way we could work around this is track uninhabitedness but only take advantage of it in enums.
(
I am not aware of a solution that works withvariant types, which I think we should keep in mind.)EDIT: we can disallow creating the
enum
from an incomplete variant. This is also required by niche optimizations because the niche slot being uninitialized would result in UB.EDIT2: incomplete variants might be possible today already (#49298 (comment)).
As an example, in debug mode, this snippet prints
0
, becausex.1
andy
overlap:cc @nikomatsakis @nagisa
The text was updated successfully, but these errors were encountered: