-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
"incorrect use of uninit memory" lint should fire for multi-variant enum #73608
Comments
I was shortly confused why one variant enums can be okay, so here's a short example: enum Test {
Variant(u8),
} Note that we should also not warn on an enum with |
To be clear, this is not a guarantee. It's just how we currently represent enums. It might become UB in the future to leave such an enum uninit. Right now, it is a case of incorrectly relying on unspecified data layout.
That is not correct, we should warn for such cases. Uninitialized memory is different from memory having some arbitrary value. So when we say "the tag has to encode a valid discriminant", then even if every initialized bit pattern encodes a valid discriminant, that does not mean that the uninitialized bit pattern encodes a valid discriminant. |
In light of this, I wonder if we should just warn about all enums, to avoid relying on such unspecified details? However that would mean that we'd warn about cases that are not UB... so we should likely at least make the message different. |
Don't both cases have the same reasoning though? I don't quite see why you do not want to warn on
Afaict we check this during code-gen, not at run-time. So we could emit post monomorphization warnings here, which we probably do not want, e.g. use std::{mem, ptr};
unsafe trait Foo {
const IS_POD: bool;
}
unsafe impl Foo for bool {
const IS_POD: bool = false;
}
unsafe impl Foo for u8 {
const IS_POD: bool = true;
}
fn bar<T: Foo + Default>() -> T {
unsafe {
if T::IS_POD {
let mut x = mem::uninitialized();
ptr::write(&mut x, T::default());
x
} else {
T::default()
}
}
}
fn main() {
let _: bool = bar();
let _: u8 = bar();
} |
Isn't There is obviously a somewhat important difference between theoretical and "actual" UB here, which is probably also the line we draw when deciding if the call itself should panic. IMO we should warn for anything which is "theoretically" UB while panicking in cases which are currently known to sometimes to cause problems. |
We do, sorry for the vagueness. But indeed post-monomorphization time errors would likely not even show up in the right crate here, and they would break proper run-time tests as you noted.
No, it's very different. Your single-variant enum is (under current rustc layout) equivalent to For enums with a tag, I have not seen anyone suggest to permit them being uninitialized under any circumstances. It would we an awful special case in our semantics, and I don't see any reason to do this.
That is not currently our approach e.g. for uninitialized |
I tried this code:
I expected to see this happen: It should warn about
Fruit
not working withmem::uninitialized
.Instead, this happened: There is no diagnostic.
The problem here is that not all enums should cause the warning. If the enum only has one variant, being uninitialized could actually be fine (but variants that are uninhabited and ZST do not count). This is easy to check at run-time where we have the full layout (so we panic), but statically we just have the type and that makes it harder.
As a start, we could count the fieldless variants; if there are at least 2 then the enum definitely needs a tag and thus may not remain uninitialized.
Cc @stepancheg
The text was updated successfully, but these errors were encountered: