about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2022-09-25 22:56:01 +0200
committerRalf Jung <post@ralfj.de>2022-09-26 09:44:10 +0200
commitc19daa472b69fdcbbb15402e8d1e8e05bae39f38 (patch)
treefc592cc8e6edd8569da64bb5ee112d7d79724a58
parentf3fafbb006ee98635874f73e480655912b465e65 (diff)
downloadrust-c19daa472b69fdcbbb15402e8d1e8e05bae39f38.tar.gz
rust-c19daa472b69fdcbbb15402e8d1e8e05bae39f38.zip
make invalid_value lint a bit smarter around enums
-rw-r--r--compiler/rustc_lint/src/builtin.rs160
-rw-r--r--src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr8
-rw-r--r--src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr8
-rw-r--r--src/test/ui/lint/invalid_value.rs (renamed from src/test/ui/lint/uninitialized-zeroed.rs)30
-rw-r--r--src/test/ui/lint/invalid_value.stderr (renamed from src/test/ui/lint/uninitialized-zeroed.stderr)207
-rw-r--r--src/test/ui/statics/uninhabited-static.stderr12
6 files changed, 298 insertions, 127 deletions
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 5fba1d3d3c7..529e709c5db 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -46,8 +46,7 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::layout::{LayoutError, LayoutOf};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::Instance;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef};
 use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
 use rustc_span::edition::Edition;
 use rustc_span::source_map::Spanned;
@@ -2425,12 +2424,56 @@ 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<'tcx>(adt: ty::AdtDef<'tcx>) -> 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
+        /// Determines whether the given type is inhabited. `None` means that we don't know.
+        fn ty_inhabited(ty: Ty<'_>) -> Option<bool> {
+            use rustc_type_ir::sty::TyKind::*;
+            match ty.kind() {
+                Never => Some(false),
+                Int(_) | Uint(_) | Float(_) | Bool | Char | RawPtr(_) => Some(true),
+                // Fallback for more complicated types. (Note that `&!` might be considered
+                // uninhabited so references are "complicated", too.)
+                _ => None,
+            }
+        }
+        /// Determines whether a product type formed from a list of types is inhabited.
+        fn tys_inhabited<'tcx>(tys: impl Iterator<Item = Ty<'tcx>>) -> Option<bool> {
+            let mut definitely_inhabited = true; // with no fields, we are definitely inhabited.
+            for ty in tys {
+                match ty_inhabited(ty) {
+                    // If any type is uninhabited, the product is uninhabited.
+                    Some(false) => return Some(false),
+                    // Otherwise go searching for a `None`.
+                    None => {
+                        // We don't know.
+                        definitely_inhabited = false;
+                    }
+                    Some(true) => {}
+                }
+            }
+            if definitely_inhabited { Some(true) } else { None }
+        }
+
+        fn variant_find_init_error<'tcx>(
+            cx: &LateContext<'tcx>,
+            variant: &VariantDef,
+            substs: ty::SubstsRef<'tcx>,
+            descr: &str,
+            init: InitKind,
+        ) -> Option<InitError> {
+            variant.fields.iter().find_map(|field| {
+                ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(|(mut msg, span)| {
+                    if span.is_none() {
+                        // Point to this field, should be helpful for figuring
+                        // out where the source of the error is.
+                        let span = cx.tcx.def_span(field.did);
+                        write!(&mut msg, " (in this {descr})").unwrap();
+                        (msg, Some(span))
+                    } else {
+                        // Just forward.
+                        (msg, span)
+                    }
+                })
+            })
         }
 
         /// Return `Some` only if we are sure this type does *not*
@@ -2468,7 +2511,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
                 RawPtr(_) if init == InitKind::Uninit => {
                     Some(("raw pointers must not be uninitialized".to_string(), None))
                 }
-                // Recurse and checks for some compound types.
+                // Recurse and checks for some compound types. (but not unions)
                 Adt(adt_def, substs) if !adt_def.is_union() => {
                     // First check if this ADT has a layout attribute (like `NonNull` and friends).
                     use std::ops::Bound;
@@ -2476,6 +2519,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
                         // We exploit here that `layout_scalar_valid_range` will never
                         // return `Bound::Excluded`.  (And we have tests checking that we
                         // handle the attribute correctly.)
+                        // We don't add a span since users cannot declare such types anyway.
                         (Bound::Included(lo), _) if lo > 0 => {
                             return Some((format!("`{}` must be non-null", ty), None));
                         }
@@ -2492,50 +2536,64 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
                         }
                         _ => {}
                     }
-                    // Now, recurse.
-                    match adt_def.variants().len() {
-                        0 => Some(("enums with no variants have no valid value".to_string(), None)),
-                        1 => {
-                            // Struct, or enum with exactly one variant.
-                            // Proceed recursively, check all fields.
-                            let variant = &adt_def.variant(VariantIdx::from_u32(0));
-                            variant.fields.iter().find_map(|field| {
-                                ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(
-                                    |(mut msg, span)| {
-                                        if span.is_none() {
-                                            // Point to this field, should be helpful for figuring
-                                            // out where the source of the error is.
-                                            let span = cx.tcx.def_span(field.did);
-                                            write!(
-                                                &mut msg,
-                                                " (in this {} field)",
-                                                adt_def.descr()
-                                            )
-                                            .unwrap();
-                                            (msg, Some(span))
-                                        } else {
-                                            // Just forward.
-                                            (msg, span)
-                                        }
-                                    },
-                                )
-                            })
-                        }
-                        // Multi-variant enum.
-                        _ => {
-                            if init == InitKind::Uninit && is_multi_variant(*adt_def) {
-                                let span = cx.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
-                            }
+                    // Handle structs.
+                    if adt_def.is_struct() {
+                        return variant_find_init_error(
+                            cx,
+                            adt_def.non_enum_variant(),
+                            substs,
+                            "struct field",
+                            init,
+                        );
+                    }
+                    // And now, enums.
+                    let span = cx.tcx.def_span(adt_def.did());
+                    let mut potential_variants = adt_def.variants().iter().filter_map(|variant| {
+                        let inhabited = tys_inhabited(
+                            variant.fields.iter().map(|field| field.ty(cx.tcx, substs)),
+                        );
+                        let definitely_inhabited = match inhabited {
+                            // Entirely skip uninhbaited variants.
+                            Some(false) => return None,
+                            // Forward the others, but remember which ones are definitely inhabited.
+                            Some(true) => true,
+                            None => false,
+                        };
+                        Some((variant, definitely_inhabited))
+                    });
+                    let Some(first_variant) = potential_variants.next() else {
+                        return Some(("enums with no inhabited variants have no valid value".to_string(), Some(span)));
+                    };
+                    // So we have at least one potentially inhabited variant. Might we have two?
+                    let Some(second_variant) = potential_variants.next() else {
+                        // There is only one potentially inhabited variant. So we can recursively check that variant!
+                        return variant_find_init_error(
+                            cx,
+                            &first_variant.0,
+                            substs,
+                            "field of the only potentially inhabited enum variant",
+                            init,
+                        );
+                    };
+                    // So we have at least two potentially inhabited variants.
+                    // If we can prove that we have at least two *definitely* inhabited variants,
+                    // then we have a tag and hence leaving this uninit is definitely disallowed.
+                    // (Leaving it zeroed could be okay, depending on which variant is encoded as zero tag.)
+                    if init == InitKind::Uninit {
+                        let definitely_inhabited = (first_variant.1 as usize)
+                            + (second_variant.1 as usize)
+                            + potential_variants
+                                .filter(|(_variant, definitely_inhabited)| *definitely_inhabited)
+                                .count();
+                        if definitely_inhabited > 1 {
+                            return Some((
+                                "enums with multiple inhabited variants have to be initialized to a variant".to_string(),
+                                Some(span),
+                            ));
                         }
                     }
+                    // We couldn't find anything wrong here.
+                    None
                 }
                 Tuple(..) => {
                     // Proceed recursively, check all fields.
diff --git a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr
index bdaeb4a0fbe..b568518b449 100644
--- a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr
+++ b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr
@@ -40,11 +40,11 @@ LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
    |                                          this code causes undefined behavior when executed
    |                                          help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-note: enums with no variants have no valid value (in this struct field)
-  --> $DIR/validate_uninhabited_zsts.rs:16:22
+note: enums with no inhabited variants have no valid value
+  --> $DIR/validate_uninhabited_zsts.rs:13:5
    |
-LL |     pub struct Empty(Void);
-   |                      ^^^^
+LL |     enum Void {}
+   |     ^^^^^^^^^
 
 error: aborting due to 2 previous errors; 2 warnings emitted
 
diff --git a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr
index bdaeb4a0fbe..b568518b449 100644
--- a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr
+++ b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr
@@ -40,11 +40,11 @@ LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
    |                                          this code causes undefined behavior when executed
    |                                          help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-note: enums with no variants have no valid value (in this struct field)
-  --> $DIR/validate_uninhabited_zsts.rs:16:22
+note: enums with no inhabited variants have no valid value
+  --> $DIR/validate_uninhabited_zsts.rs:13:5
    |
-LL |     pub struct Empty(Void);
-   |                      ^^^^
+LL |     enum Void {}
+   |     ^^^^^^^^^
 
 error: aborting due to 2 previous errors; 2 warnings emitted
 
diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/invalid_value.rs
index dae258407eb..c37a472ffe1 100644
--- a/src/test/ui/lint/uninitialized-zeroed.rs
+++ b/src/test/ui/lint/invalid_value.rs
@@ -34,6 +34,16 @@ enum OneFruit {
     Banana,
 }
 
+enum OneFruitNonZero {
+    Apple(!),
+    Banana(NonZeroU32),
+}
+
+enum TwoUninhabited {
+    A(!),
+    B(!),
+}
+
 #[allow(unused)]
 fn generic<T: 'static>() {
     unsafe {
@@ -84,6 +94,12 @@ fn main() {
         let _val: [fn(); 2] = mem::zeroed(); //~ ERROR: does not permit zero-initialization
         let _val: [fn(); 2] = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
 
+        let _val: TwoUninhabited = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: TwoUninhabited = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: OneFruitNonZero = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: OneFruitNonZero = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
         // Things that can be zero, but not uninit.
         let _val: bool = mem::zeroed();
         let _val: bool = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
@@ -112,6 +128,16 @@ fn main() {
         let _val: *const [()] = mem::zeroed();
         let _val: *const [()] = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
 
+        // Things where 0 is okay due to rustc implementation details,
+        // but that are not guaranteed to keep working.
+        let _val: Result<i32, i32> = mem::zeroed();
+        let _val: Result<i32, i32> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        // Some things that happen to work due to rustc implementation details,
+        // but are not guaranteed to keep working.
+        let _val: OneFruit = mem::zeroed();
+        let _val: OneFruit = mem::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
@@ -129,9 +155,5 @@ fn main() {
         let _val: bool = MaybeUninit::zeroed().assume_init();
         let _val: [bool; 0] = MaybeUninit::uninit().assume_init();
         let _val: [!; 0] = MaybeUninit::zeroed().assume_init();
-
-        // Some things that happen to work due to rustc implementation details,
-        // but are not guaranteed to keep working.
-        let _val: OneFruit = mem::uninitialized();
     }
 }
diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/invalid_value.stderr
index b46042e7be4..750b3c76c44 100644
--- a/src/test/ui/lint/uninitialized-zeroed.stderr
+++ b/src/test/ui/lint/invalid_value.stderr
@@ -1,5 +1,5 @@
 error: the type `&T` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:40:32
+  --> $DIR/invalid_value.rs:50:32
    |
 LL |         let _val: &'static T = mem::zeroed();
    |                                ^^^^^^^^^^^^^
@@ -8,14 +8,14 @@ LL |         let _val: &'static T = mem::zeroed();
    |                                help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
 note: the lint level is defined here
-  --> $DIR/uninitialized-zeroed.rs:6:9
+  --> $DIR/invalid_value.rs:6:9
    |
 LL | #![deny(invalid_value)]
    |         ^^^^^^^^^^^^^
    = note: references must be non-null
 
 error: the type `&T` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:41:32
+  --> $DIR/invalid_value.rs:51: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:43:38
+  --> $DIR/invalid_value.rs:53:38
    |
 LL |         let _val: Wrap<&'static T> = mem::zeroed();
    |                                      ^^^^^^^^^^^^^
@@ -35,13 +35,13 @@ LL |         let _val: Wrap<&'static T> = mem::zeroed();
    |                                      help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
 note: references must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:17:18
+  --> $DIR/invalid_value.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
    |                  ^^^^^^^^^^
 
 error: the type `Wrap<&T>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:44:38
+  --> $DIR/invalid_value.rs:54:38
    |
 LL |         let _val: Wrap<&'static T> = mem::uninitialized();
    |                                      ^^^^^^^^^^^^^^^^^^^^
@@ -50,13 +50,13 @@ LL |         let _val: Wrap<&'static T> = mem::uninitialized();
    |                                      help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
 note: references must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:17:18
+  --> $DIR/invalid_value.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
    |                  ^^^^^^^^^^
 
 error: the type `!` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:51:23
+  --> $DIR/invalid_value.rs:61: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:52:23
+  --> $DIR/invalid_value.rs:62: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:54:30
+  --> $DIR/invalid_value.rs:64: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:55:30
+  --> $DIR/invalid_value.rs:65:30
    |
 LL |         let _val: (i32, !) = mem::uninitialized();
    |                              ^^^^^^^^^^^^^^^^^^^^
@@ -100,7 +100,7 @@ LL |         let _val: (i32, !) = mem::uninitialized();
    = note: integers must not be uninitialized
 
 error: the type `Void` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:57:26
+  --> $DIR/invalid_value.rs:67:26
    |
 LL |         let _val: Void = mem::zeroed();
    |                          ^^^^^^^^^^^^^
@@ -108,10 +108,14 @@ LL |         let _val: Void = mem::zeroed();
    |                          this code causes undefined behavior when executed
    |                          help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-   = note: enums with no variants have no valid value
+note: enums with no inhabited variants have no valid value
+  --> $DIR/invalid_value.rs:12:1
+   |
+LL | enum Void {}
+   | ^^^^^^^^^
 
 error: the type `Void` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:58:26
+  --> $DIR/invalid_value.rs:68:26
    |
 LL |         let _val: Void = mem::uninitialized();
    |                          ^^^^^^^^^^^^^^^^^^^^
@@ -119,10 +123,14 @@ LL |         let _val: Void = mem::uninitialized();
    |                          this code causes undefined behavior when executed
    |                          help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-   = note: enums with no variants have no valid value
+note: enums with no inhabited variants have no valid value
+  --> $DIR/invalid_value.rs:12:1
+   |
+LL | enum Void {}
+   | ^^^^^^^^^
 
 error: the type `&i32` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:60:34
+  --> $DIR/invalid_value.rs:70:34
    |
 LL |         let _val: &'static i32 = mem::zeroed();
    |                                  ^^^^^^^^^^^^^
@@ -133,7 +141,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:61:34
+  --> $DIR/invalid_value.rs:71:34
    |
 LL |         let _val: &'static i32 = mem::uninitialized();
    |                                  ^^^^^^^^^^^^^^^^^^^^
@@ -144,7 +152,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:63:25
+  --> $DIR/invalid_value.rs:73:25
    |
 LL |         let _val: Ref = mem::zeroed();
    |                         ^^^^^^^^^^^^^
@@ -153,13 +161,13 @@ LL |         let _val: Ref = mem::zeroed();
    |                         help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
 note: references must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:14:12
+  --> $DIR/invalid_value.rs:14:12
    |
 LL | struct Ref(&'static i32);
    |            ^^^^^^^^^^^^
 
 error: the type `Ref` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:64:25
+  --> $DIR/invalid_value.rs:74:25
    |
 LL |         let _val: Ref = mem::uninitialized();
    |                         ^^^^^^^^^^^^^^^^^^^^
@@ -168,13 +176,13 @@ LL |         let _val: Ref = mem::uninitialized();
    |                         help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
 note: references must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:14:12
+  --> $DIR/invalid_value.rs:14:12
    |
 LL | struct Ref(&'static i32);
    |            ^^^^^^^^^^^^
 
 error: the type `fn()` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:66:26
+  --> $DIR/invalid_value.rs:76:26
    |
 LL |         let _val: fn() = mem::zeroed();
    |                          ^^^^^^^^^^^^^
@@ -185,7 +193,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:67:26
+  --> $DIR/invalid_value.rs:77:26
    |
 LL |         let _val: fn() = mem::uninitialized();
    |                          ^^^^^^^^^^^^^^^^^^^^
@@ -196,7 +204,7 @@ LL |         let _val: fn() = mem::uninitialized();
    = note: function pointers must be non-null
 
 error: the type `Wrap<fn()>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:69:32
+  --> $DIR/invalid_value.rs:79:32
    |
 LL |         let _val: Wrap<fn()> = mem::zeroed();
    |                                ^^^^^^^^^^^^^
@@ -205,13 +213,13 @@ LL |         let _val: Wrap<fn()> = mem::zeroed();
    |                                help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
 note: function pointers must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:17:18
+  --> $DIR/invalid_value.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
    |                  ^^^^^^^^^^
 
 error: the type `Wrap<fn()>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:70:32
+  --> $DIR/invalid_value.rs:80:32
    |
 LL |         let _val: Wrap<fn()> = mem::uninitialized();
    |                                ^^^^^^^^^^^^^^^^^^^^
@@ -220,13 +228,13 @@ LL |         let _val: Wrap<fn()> = mem::uninitialized();
    |                                help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
 note: function pointers must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:17:18
+  --> $DIR/invalid_value.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
    |                  ^^^^^^^^^^
 
 error: the type `WrapEnum<fn()>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:72:36
+  --> $DIR/invalid_value.rs:82:36
    |
 LL |         let _val: WrapEnum<fn()> = mem::zeroed();
    |                                    ^^^^^^^^^^^^^
@@ -234,14 +242,14 @@ LL |         let _val: WrapEnum<fn()> = mem::zeroed();
    |                                    this code causes undefined behavior when executed
    |                                    help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-note: function pointers must be non-null (in this enum field)
-  --> $DIR/uninitialized-zeroed.rs:18:28
+note: function pointers must be non-null (in this field of the only potentially inhabited enum variant)
+  --> $DIR/invalid_value.rs:18:28
    |
 LL | enum WrapEnum<T> { Wrapped(T) }
    |                            ^
 
 error: the type `WrapEnum<fn()>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:73:36
+  --> $DIR/invalid_value.rs:83:36
    |
 LL |         let _val: WrapEnum<fn()> = mem::uninitialized();
    |                                    ^^^^^^^^^^^^^^^^^^^^
@@ -249,14 +257,14 @@ LL |         let _val: WrapEnum<fn()> = mem::uninitialized();
    |                                    this code causes undefined behavior when executed
    |                                    help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-note: function pointers must be non-null (in this enum field)
-  --> $DIR/uninitialized-zeroed.rs:18:28
+note: function pointers must be non-null (in this field of the only potentially inhabited enum variant)
+  --> $DIR/invalid_value.rs:18:28
    |
 LL | enum WrapEnum<T> { Wrapped(T) }
    |                            ^
 
 error: the type `Wrap<(RefPair, i32)>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:75:42
+  --> $DIR/invalid_value.rs:85:42
    |
 LL |         let _val: Wrap<(RefPair, i32)> = mem::zeroed();
    |                                          ^^^^^^^^^^^^^
@@ -265,13 +273,13 @@ LL |         let _val: Wrap<(RefPair, i32)> = mem::zeroed();
    |                                          help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
 note: references must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:15:16
+  --> $DIR/invalid_value.rs:15:16
    |
 LL | struct RefPair((&'static i32, i32));
    |                ^^^^^^^^^^^^^^^^^^^
 
 error: the type `Wrap<(RefPair, i32)>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:76:42
+  --> $DIR/invalid_value.rs:86:42
    |
 LL |         let _val: Wrap<(RefPair, i32)> = mem::uninitialized();
    |                                          ^^^^^^^^^^^^^^^^^^^^
@@ -280,13 +288,13 @@ LL |         let _val: Wrap<(RefPair, i32)> = mem::uninitialized();
    |                                          help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
 note: references must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:15:16
+  --> $DIR/invalid_value.rs:15:16
    |
 LL | struct RefPair((&'static i32, i32));
    |                ^^^^^^^^^^^^^^^^^^^
 
 error: the type `NonNull<i32>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:78:34
+  --> $DIR/invalid_value.rs:88:34
    |
 LL |         let _val: NonNull<i32> = mem::zeroed();
    |                                  ^^^^^^^^^^^^^
@@ -297,7 +305,7 @@ LL |         let _val: NonNull<i32> = mem::zeroed();
    = note: `std::ptr::NonNull<i32>` must be non-null
 
 error: the type `NonNull<i32>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:79:34
+  --> $DIR/invalid_value.rs:89:34
    |
 LL |         let _val: NonNull<i32> = mem::uninitialized();
    |                                  ^^^^^^^^^^^^^^^^^^^^
@@ -308,7 +316,7 @@ LL |         let _val: NonNull<i32> = mem::uninitialized();
    = note: `std::ptr::NonNull<i32>` must be non-null
 
 error: the type `*const dyn Send` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:81:37
+  --> $DIR/invalid_value.rs:91:37
    |
 LL |         let _val: *const dyn Send = mem::zeroed();
    |                                     ^^^^^^^^^^^^^
@@ -319,7 +327,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 Send` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:82:37
+  --> $DIR/invalid_value.rs:92:37
    |
 LL |         let _val: *const dyn Send = mem::uninitialized();
    |                                     ^^^^^^^^^^^^^^^^^^^^
@@ -330,7 +338,7 @@ LL |         let _val: *const dyn Send = mem::uninitialized();
    = note: the vtable of a wide raw pointer must be non-null
 
 error: the type `[fn(); 2]` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:84:31
+  --> $DIR/invalid_value.rs:94:31
    |
 LL |         let _val: [fn(); 2] = mem::zeroed();
    |                               ^^^^^^^^^^^^^
@@ -341,7 +349,7 @@ LL |         let _val: [fn(); 2] = mem::zeroed();
    = note: function pointers must be non-null
 
 error: the type `[fn(); 2]` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:85:31
+  --> $DIR/invalid_value.rs:95:31
    |
 LL |         let _val: [fn(); 2] = mem::uninitialized();
    |                               ^^^^^^^^^^^^^^^^^^^^
@@ -351,8 +359,68 @@ LL |         let _val: [fn(); 2] = mem::uninitialized();
    |
    = note: function pointers must be non-null
 
+error: the type `TwoUninhabited` does not permit zero-initialization
+  --> $DIR/invalid_value.rs:97:36
+   |
+LL |         let _val: TwoUninhabited = mem::zeroed();
+   |                                    ^^^^^^^^^^^^^
+   |                                    |
+   |                                    this code causes undefined behavior when executed
+   |                                    help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+   |
+note: enums with no inhabited variants have no valid value
+  --> $DIR/invalid_value.rs:42:1
+   |
+LL | enum TwoUninhabited {
+   | ^^^^^^^^^^^^^^^^^^^
+
+error: the type `TwoUninhabited` does not permit being left uninitialized
+  --> $DIR/invalid_value.rs:98:36
+   |
+LL |         let _val: TwoUninhabited = mem::uninitialized();
+   |                                    ^^^^^^^^^^^^^^^^^^^^
+   |                                    |
+   |                                    this code causes undefined behavior when executed
+   |                                    help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+   |
+note: enums with no inhabited variants have no valid value
+  --> $DIR/invalid_value.rs:42:1
+   |
+LL | enum TwoUninhabited {
+   | ^^^^^^^^^^^^^^^^^^^
+
+error: the type `OneFruitNonZero` does not permit zero-initialization
+  --> $DIR/invalid_value.rs:100:37
+   |
+LL |         let _val: OneFruitNonZero = mem::zeroed();
+   |                                     ^^^^^^^^^^^^^
+   |                                     |
+   |                                     this code causes undefined behavior when executed
+   |                                     help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+   |
+note: `std::num::NonZeroU32` must be non-null (in this field of the only potentially inhabited enum variant)
+  --> $DIR/invalid_value.rs:39:12
+   |
+LL |     Banana(NonZeroU32),
+   |            ^^^^^^^^^^
+
+error: the type `OneFruitNonZero` does not permit being left uninitialized
+  --> $DIR/invalid_value.rs:101:37
+   |
+LL |         let _val: OneFruitNonZero = mem::uninitialized();
+   |                                     ^^^^^^^^^^^^^^^^^^^^
+   |                                     |
+   |                                     this code causes undefined behavior when executed
+   |                                     help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+   |
+note: `std::num::NonZeroU32` must be non-null (in this field of the only potentially inhabited enum variant)
+  --> $DIR/invalid_value.rs:39:12
+   |
+LL |     Banana(NonZeroU32),
+   |            ^^^^^^^^^^
+
 error: the type `bool` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:89:26
+  --> $DIR/invalid_value.rs:105:26
    |
 LL |         let _val: bool = mem::uninitialized();
    |                          ^^^^^^^^^^^^^^^^^^^^
@@ -363,7 +431,7 @@ LL |         let _val: bool = mem::uninitialized();
    = note: booleans must be either `true` or `false`
 
 error: the type `Wrap<char>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:92:32
+  --> $DIR/invalid_value.rs:108:32
    |
 LL |         let _val: Wrap<char> = mem::uninitialized();
    |                                ^^^^^^^^^^^^^^^^^^^^
@@ -372,13 +440,13 @@ LL |         let _val: Wrap<char> = mem::uninitialized();
    |                                help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
 note: characters must be a valid Unicode codepoint (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:17:18
+  --> $DIR/invalid_value.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
    |                  ^^^^^^^^^^
 
 error: the type `NonBig` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:95:28
+  --> $DIR/invalid_value.rs:111:28
    |
 LL |         let _val: NonBig = mem::uninitialized();
    |                            ^^^^^^^^^^^^^^^^^^^^
@@ -389,7 +457,7 @@ 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:98:27
+  --> $DIR/invalid_value.rs:114:27
    |
 LL |         let _val: Fruit = mem::uninitialized();
    |                           ^^^^^^^^^^^^^^^^^^^^
@@ -397,14 +465,14 @@ LL |         let _val: Fruit = mem::uninitialized();
    |                           this code causes undefined behavior when executed
    |                           help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-note: enums have to be initialized to a variant
-  --> $DIR/uninitialized-zeroed.rs:26:1
+note: enums with multiple inhabited variants have to be initialized to a variant
+  --> $DIR/invalid_value.rs:26:1
    |
 LL | enum Fruit {
    | ^^^^^^^^^^
 
 error: the type `[bool; 2]` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:101:31
+  --> $DIR/invalid_value.rs:117:31
    |
 LL |         let _val: [bool; 2] = mem::uninitialized();
    |                               ^^^^^^^^^^^^^^^^^^^^
@@ -415,7 +483,7 @@ LL |         let _val: [bool; 2] = mem::uninitialized();
    = note: booleans must be either `true` or `false`
 
 error: the type `i32` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:104:25
+  --> $DIR/invalid_value.rs:120:25
    |
 LL |         let _val: i32 = mem::uninitialized();
    |                         ^^^^^^^^^^^^^^^^^^^^
@@ -426,7 +494,7 @@ LL |         let _val: i32 = mem::uninitialized();
    = note: integers must not be uninitialized
 
 error: the type `f32` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:107:25
+  --> $DIR/invalid_value.rs:123:25
    |
 LL |         let _val: f32 = mem::uninitialized();
    |                         ^^^^^^^^^^^^^^^^^^^^
@@ -437,7 +505,7 @@ LL |         let _val: f32 = mem::uninitialized();
    = note: floats must not be uninitialized
 
 error: the type `*const ()` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:110:31
+  --> $DIR/invalid_value.rs:126:31
    |
 LL |         let _val: *const () = mem::uninitialized();
    |                               ^^^^^^^^^^^^^^^^^^^^
@@ -448,7 +516,7 @@ LL |         let _val: *const () = mem::uninitialized();
    = note: raw pointers must not be uninitialized
 
 error: the type `*const [()]` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:113:33
+  --> $DIR/invalid_value.rs:129:33
    |
 LL |         let _val: *const [()] = mem::uninitialized();
    |                                 ^^^^^^^^^^^^^^^^^^^^
@@ -458,8 +526,23 @@ LL |         let _val: *const [()] = mem::uninitialized();
    |
    = note: raw pointers must not be uninitialized
 
+error: the type `Result<i32, i32>` does not permit being left uninitialized
+  --> $DIR/invalid_value.rs:134:38
+   |
+LL |         let _val: Result<i32, i32> = mem::uninitialized();
+   |                                      ^^^^^^^^^^^^^^^^^^^^
+   |                                      |
+   |                                      this code causes undefined behavior when executed
+   |                                      help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+   |
+note: enums with multiple inhabited variants have to be initialized to a variant
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+   |
+LL | pub enum Result<T, E> {
+   | ^^^^^^^^^^^^^^^^^^^^^
+
 error: the type `&i32` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:116:34
+  --> $DIR/invalid_value.rs:142:34
    |
 LL |         let _val: &'static i32 = mem::transmute(0usize);
    |                                  ^^^^^^^^^^^^^^^^^^^^^^
@@ -470,7 +553,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:117:36
+  --> $DIR/invalid_value.rs:143:36
    |
 LL |         let _val: &'static [i32] = mem::transmute((0usize, 0usize));
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -481,7 +564,7 @@ LL |         let _val: &'static [i32] = mem::transmute((0usize, 0usize));
    = note: references must be non-null
 
 error: the type `NonZeroU32` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:118:32
+  --> $DIR/invalid_value.rs:144:32
    |
 LL |         let _val: NonZeroU32 = mem::transmute(0);
    |                                ^^^^^^^^^^^^^^^^^
@@ -492,7 +575,7 @@ LL |         let _val: NonZeroU32 = mem::transmute(0);
    = note: `std::num::NonZeroU32` must be non-null
 
 error: the type `NonNull<i32>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:121:34
+  --> $DIR/invalid_value.rs:147:34
    |
 LL |         let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init();
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -503,7 +586,7 @@ LL |         let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init();
    = note: `std::ptr::NonNull<i32>` must be non-null
 
 error: the type `NonNull<i32>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:122:34
+  --> $DIR/invalid_value.rs:148:34
    |
 LL |         let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -514,7 +597,7 @@ LL |         let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
    = note: `std::ptr::NonNull<i32>` must be non-null
 
 error: the type `bool` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:123:26
+  --> $DIR/invalid_value.rs:149:26
    |
 LL |         let _val: bool = MaybeUninit::uninit().assume_init();
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -524,5 +607,5 @@ LL |         let _val: bool = MaybeUninit::uninit().assume_init();
    |
    = note: booleans must be either `true` or `false`
 
-error: aborting due to 43 previous errors
+error: aborting due to 48 previous errors
 
diff --git a/src/test/ui/statics/uninhabited-static.stderr b/src/test/ui/statics/uninhabited-static.stderr
index 88ee4cbdc2e..6d37de8ff3f 100644
--- a/src/test/ui/statics/uninhabited-static.stderr
+++ b/src/test/ui/statics/uninhabited-static.stderr
@@ -59,7 +59,11 @@ LL | static VOID2: Void = unsafe { std::mem::transmute(()) };
    |                               help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
    = note: `#[warn(invalid_value)]` on by default
-   = note: enums with no variants have no valid value
+note: enums with no inhabited variants have no valid value
+  --> $DIR/uninhabited-static.rs:4:1
+   |
+LL | enum Void {}
+   | ^^^^^^^^^
 
 error[E0080]: could not evaluate static initializer
   --> $DIR/uninhabited-static.rs:16:32
@@ -76,7 +80,11 @@ LL | static NEVER2: Void = unsafe { std::mem::transmute(()) };
    |                                this code causes undefined behavior when executed
    |                                help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-   = note: enums with no variants have no valid value
+note: enums with no inhabited variants have no valid value
+  --> $DIR/uninhabited-static.rs:4:1
+   |
+LL | enum Void {}
+   | ^^^^^^^^^
 
 error: aborting due to 6 previous errors; 2 warnings emitted