diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 69b564c573d6a..5c4be715add79 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1922,6 +1922,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { None } + /// Test if this enum has several actually "existing" variants. + /// Zero-sized uninhabited variants do not always have a tag assigned and thus do not "exist". + fn is_multi_variant(adt: &ty::AdtDef) -> bool { + // As an approximation, we only count dataless variants. Those are definitely inhabited. + let existing_variants = adt.variants.iter().filter(|v| v.fields.is_empty()).count(); + existing_variants > 1 + } + /// Return `Some` only if we are sure this type does *not* /// allow zero initialization. fn ty_find_init_error<'tcx>( @@ -1950,7 +1958,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { } // Recurse and checks for some compound types. Adt(adt_def, substs) if !adt_def.is_union() => { - // First check f this ADT has a layout attribute (like `NonNull` and friends). + // First check if this ADT has a layout attribute (like `NonNull` and friends). use std::ops::Bound; match tcx.layout_scalar_valid_range(adt_def.did) { // We exploit here that `layout_scalar_valid_range` will never @@ -2001,10 +2009,20 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { ) }) } - // Multi-variant enums are tricky: if all but one variant are - // uninhabited, we might actually do layout like for a single-variant - // enum, and then even leaving them uninitialized could be okay. - _ => None, // Conservative fallback for multi-variant enum. + // Multi-variant enum. + _ => { + if init == InitKind::Uninit && is_multi_variant(adt_def) { + let span = tcx.def_span(adt_def.did); + Some(( + "enums have to be initialized to a variant".to_string(), + Some(span), + )) + } else { + // In principle, for zero-initialization we could figure out which variant corresponds + // to tag 0, and check that... but for now we just accept all zero-initializations. + None + } + } } } Tuple(..) => { diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs index 473be434a7524..78d3060886dd0 100644 --- a/src/test/ui/lint/uninitialized-zeroed.rs +++ b/src/test/ui/lint/uninitialized-zeroed.rs @@ -23,6 +23,18 @@ enum WrapEnum { Wrapped(T) } #[repr(transparent)] pub(crate) struct NonBig(u64); +/// A two-variant enum, thus needs a tag and may not remain uninitialized. +enum Fruit { + Apple, + Banana, +} + +/// Looks like two variants but really only has one. +enum OneFruit { + Apple(!), + Banana, +} + #[allow(unused)] fn generic() { unsafe { @@ -80,6 +92,9 @@ fn main() { let _val: NonBig = mem::zeroed(); let _val: NonBig = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: Fruit = mem::zeroed(); + let _val: Fruit = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + // Transmute-from-0 let _val: &'static i32 = mem::transmute(0usize); //~ ERROR: does not permit zero-initialization let _val: &'static [i32] = mem::transmute((0usize, 0usize)); //~ ERROR: does not permit zero-initialization @@ -96,5 +111,9 @@ fn main() { let _val: MaybeUninit<&'static i32> = mem::zeroed(); let _val: i32 = mem::zeroed(); let _val: bool = MaybeUninit::zeroed().assume_init(); + // Some things that happen to work due to rustc implementation details, + // but are not guaranteed to keep working. + let _val: i32 = mem::uninitialized(); + let _val: OneFruit = mem::uninitialized(); } } diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index bf0562713a497..3bf8a66ab0ae5 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -1,5 +1,5 @@ error: the type `&T` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:29:32 + --> $DIR/uninitialized-zeroed.rs:41:32 | LL | let _val: &'static T = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | #![deny(invalid_value)] = note: references must be non-null error: the type `&T` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:30:32 + --> $DIR/uninitialized-zeroed.rs:42:32 | LL | let _val: &'static T = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let _val: &'static T = mem::uninitialized(); = note: references must be non-null error: the type `Wrap<&T>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:32:38 + --> $DIR/uninitialized-zeroed.rs:44:38 | LL | let _val: Wrap<&'static T> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `Wrap<&T>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:33:38 + --> $DIR/uninitialized-zeroed.rs:45:38 | LL | let _val: Wrap<&'static T> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `!` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:40:23 + --> $DIR/uninitialized-zeroed.rs:52:23 | LL | let _val: ! = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | let _val: ! = mem::zeroed(); = note: the `!` type has no valid value error: the type `!` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:41:23 + --> $DIR/uninitialized-zeroed.rs:53:23 | LL | let _val: ! = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -78,7 +78,7 @@ LL | let _val: ! = mem::uninitialized(); = note: the `!` type has no valid value error: the type `(i32, !)` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:43:30 + --> $DIR/uninitialized-zeroed.rs:55:30 | LL | let _val: (i32, !) = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _val: (i32, !) = mem::zeroed(); = note: the `!` type has no valid value error: the type `(i32, !)` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:44:30 + --> $DIR/uninitialized-zeroed.rs:56:30 | LL | let _val: (i32, !) = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | let _val: (i32, !) = mem::uninitialized(); = note: the `!` type has no valid value error: the type `Void` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:46:26 + --> $DIR/uninitialized-zeroed.rs:58:26 | LL | let _val: Void = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL | let _val: Void = mem::zeroed(); = note: enums with no variants have no valid value error: the type `Void` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:47:26 + --> $DIR/uninitialized-zeroed.rs:59:26 | LL | let _val: Void = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL | let _val: Void = mem::uninitialized(); = note: enums with no variants have no valid value error: the type `&i32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:49:34 + --> $DIR/uninitialized-zeroed.rs:61:34 | LL | let _val: &'static i32 = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL | let _val: &'static i32 = mem::zeroed(); = note: references must be non-null error: the type `&i32` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:50:34 + --> $DIR/uninitialized-zeroed.rs:62:34 | LL | let _val: &'static i32 = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | let _val: &'static i32 = mem::uninitialized(); = note: references must be non-null error: the type `Ref` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:52:25 + --> $DIR/uninitialized-zeroed.rs:64:25 | LL | let _val: Ref = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -159,7 +159,7 @@ LL | struct Ref(&'static i32); | ^^^^^^^^^^^^ error: the type `Ref` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:53:25 + --> $DIR/uninitialized-zeroed.rs:65:25 | LL | let _val: Ref = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -174,7 +174,7 @@ LL | struct Ref(&'static i32); | ^^^^^^^^^^^^ error: the type `fn()` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:55:26 + --> $DIR/uninitialized-zeroed.rs:67:26 | LL | let _val: fn() = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -185,7 +185,7 @@ LL | let _val: fn() = mem::zeroed(); = note: function pointers must be non-null error: the type `fn()` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:56:26 + --> $DIR/uninitialized-zeroed.rs:68:26 | LL | let _val: fn() = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -196,7 +196,7 @@ LL | let _val: fn() = mem::uninitialized(); = note: function pointers must be non-null error: the type `Wrap` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:58:32 + --> $DIR/uninitialized-zeroed.rs:70:32 | LL | let _val: Wrap = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -211,7 +211,7 @@ LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `Wrap` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:59:32 + --> $DIR/uninitialized-zeroed.rs:71:32 | LL | let _val: Wrap = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -226,7 +226,7 @@ LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `WrapEnum` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:61:36 + --> $DIR/uninitialized-zeroed.rs:73:36 | LL | let _val: WrapEnum = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL | enum WrapEnum { Wrapped(T) } | ^ error: the type `WrapEnum` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:62:36 + --> $DIR/uninitialized-zeroed.rs:74:36 | LL | let _val: WrapEnum = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -256,7 +256,7 @@ LL | enum WrapEnum { Wrapped(T) } | ^ error: the type `Wrap<(RefPair, i32)>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:64:42 + --> $DIR/uninitialized-zeroed.rs:76:42 | LL | let _val: Wrap<(RefPair, i32)> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | struct RefPair((&'static i32, i32)); | ^^^^^^^^^^^^^^^^^^^ error: the type `Wrap<(RefPair, i32)>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:65:42 + --> $DIR/uninitialized-zeroed.rs:77:42 | LL | let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -286,7 +286,7 @@ LL | struct RefPair((&'static i32, i32)); | ^^^^^^^^^^^^^^^^^^^ error: the type `std::ptr::NonNull` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:67:34 + --> $DIR/uninitialized-zeroed.rs:79:34 | LL | let _val: NonNull = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -297,7 +297,7 @@ LL | let _val: NonNull = mem::zeroed(); = note: `std::ptr::NonNull` must be non-null error: the type `std::ptr::NonNull` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:68:34 + --> $DIR/uninitialized-zeroed.rs:80:34 | LL | let _val: NonNull = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -308,7 +308,7 @@ LL | let _val: NonNull = mem::uninitialized(); = note: `std::ptr::NonNull` must be non-null error: the type `*const dyn std::marker::Send` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:70:37 + --> $DIR/uninitialized-zeroed.rs:82:37 | LL | let _val: *const dyn Send = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -319,7 +319,7 @@ LL | let _val: *const dyn Send = mem::zeroed(); = note: the vtable of a wide raw pointer must be non-null error: the type `*const dyn std::marker::Send` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:71:37 + --> $DIR/uninitialized-zeroed.rs:83:37 | LL | let _val: *const dyn Send = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -330,7 +330,7 @@ LL | let _val: *const dyn Send = mem::uninitialized(); = note: the vtable of a wide raw pointer must be non-null error: the type `bool` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:75:26 + --> $DIR/uninitialized-zeroed.rs:87:26 | LL | let _val: bool = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -341,7 +341,7 @@ LL | let _val: bool = mem::uninitialized(); = note: booleans must be either `true` or `false` error: the type `Wrap` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:78:32 + --> $DIR/uninitialized-zeroed.rs:90:32 | LL | let _val: Wrap = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -356,7 +356,7 @@ LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `NonBig` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:81:28 + --> $DIR/uninitialized-zeroed.rs:93:28 | LL | let _val: NonBig = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -366,8 +366,26 @@ LL | let _val: NonBig = mem::uninitialized(); | = note: `NonBig` must be initialized inside its custom valid range +error: the type `Fruit` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:96:27 + | +LL | let _val: Fruit = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done + | +note: enums have to be initialized to a variant + --> $DIR/uninitialized-zeroed.rs:27:1 + | +LL | / enum Fruit { +LL | | Apple, +LL | | Banana, +LL | | } + | |_^ + error: the type `&i32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:84:34 + --> $DIR/uninitialized-zeroed.rs:99:34 | LL | let _val: &'static i32 = mem::transmute(0usize); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -378,7 +396,7 @@ LL | let _val: &'static i32 = mem::transmute(0usize); = note: references must be non-null error: the type `&[i32]` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:85:36 + --> $DIR/uninitialized-zeroed.rs:100:36 | LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -389,7 +407,7 @@ LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize)); = note: references must be non-null error: the type `std::num::NonZeroU32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:86:32 + --> $DIR/uninitialized-zeroed.rs:101:32 | LL | let _val: NonZeroU32 = mem::transmute(0); | ^^^^^^^^^^^^^^^^^ @@ -400,7 +418,7 @@ LL | let _val: NonZeroU32 = mem::transmute(0); = note: `std::num::NonZeroU32` must be non-null error: the type `std::ptr::NonNull` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:89:34 + --> $DIR/uninitialized-zeroed.rs:104:34 | LL | let _val: NonNull = MaybeUninit::zeroed().assume_init(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -411,7 +429,7 @@ LL | let _val: NonNull = MaybeUninit::zeroed().assume_init(); = note: `std::ptr::NonNull` must be non-null error: the type `std::ptr::NonNull` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:90:34 + --> $DIR/uninitialized-zeroed.rs:105:34 | LL | let _val: NonNull = MaybeUninit::uninit().assume_init(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -422,7 +440,7 @@ LL | let _val: NonNull = MaybeUninit::uninit().assume_init(); = note: `std::ptr::NonNull` must be non-null error: the type `bool` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:91:26 + --> $DIR/uninitialized-zeroed.rs:106:26 | LL | let _val: bool = MaybeUninit::uninit().assume_init(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -432,5 +450,5 @@ LL | let _val: bool = MaybeUninit::uninit().assume_init(); | = note: booleans must be either `true` or `false` -error: aborting due to 35 previous errors +error: aborting due to 36 previous errors