about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2019-08-07 08:42:50 +0200
committerRalf Jung <post@ralfj.de>2019-08-11 12:04:49 +0200
commitca1e94b131eec4795f1a8dcacdb12f574e7a4a74 (patch)
tree5af7873e5a7bd5d18a442b705a6b421f4207a446
parentda6fbb18951547da08c2e353838e6b1d47d9b3be (diff)
downloadrust-ca1e94b131eec4795f1a8dcacdb12f574e7a4a74.tar.gz
rust-ca1e94b131eec4795f1a8dcacdb12f574e7a4a74.zip
warn for more cases
-rw-r--r--src/librustc_lint/builtin.rs43
-rw-r--r--src/test/ui/lint/uninitialized-zeroed.rs32
-rw-r--r--src/test/ui/lint/uninitialized-zeroed.stderr145
3 files changed, 204 insertions, 16 deletions
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index b4155646c89..c652c196afd 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -23,7 +23,7 @@
 
 use rustc::hir::def::{Res, DefKind};
 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
-use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::{self, Ty, TyCtxt, layout::VariantIdx};
 use rustc::{lint, util};
 use hir::Node;
 use util::nodemap::HirIdSet;
@@ -1879,11 +1879,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
 
         /// Return `false` only if we are sure this type does *not*
         /// allow zero initialization.
-        fn ty_maybe_allows_zero_init(ty: Ty<'_>) -> bool {
+        fn ty_maybe_allows_zero_init<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
             use rustc::ty::TyKind::*;
             match ty.sty {
                 // Primitive types that don't like 0 as a value.
                 Ref(..) | FnPtr(..) | Never => false,
+                Adt(..) if ty.is_box() => false,
+                // Recurse for some compound types.
+                Adt(adt_def, substs) if !adt_def.is_union() => {
+                    match adt_def.variants.len() {
+                        0 => false, // Uninhabited enum!
+                        1 => {
+                            // Struct, or enum with exactly one variant.
+                            // Proceed recursively, check all fields.
+                            let variant = &adt_def.variants[VariantIdx::from_u32(0)];
+                            variant.fields.iter().all(|field| {
+                                ty_maybe_allows_zero_init(
+                                    tcx,
+                                    field.ty(tcx, substs),
+                                )
+                            })
+                        }
+                        _ => true, // Conservative fallback for multi-variant enum.
+                    }
+                }
+                Tuple(substs) => {
+                    // Proceed recursively, check all fields.
+                    substs.iter().all(|field| {
+                        ty_maybe_allows_zero_init(
+                            tcx,
+                            field.expect_ty(),
+                        )
+                    })
+                }
+                // FIXME: Would be nice to also warn for `NonNull`/`NonZero*`.
                 // Conservative fallback.
                 _ => true,
             }
@@ -1900,8 +1929,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
                         // We are extremely conservative with what we warn about.
                         let conjured_ty = cx.tables.expr_ty(expr);
 
-                        if !ty_maybe_allows_zero_init(conjured_ty) {
-                            cx.span_lint(
+                        if !ty_maybe_allows_zero_init(cx.tcx, conjured_ty) {
+                            cx.struct_span_lint(
                                 INVALID_VALUE,
                                 expr.span,
                                 &format!(
@@ -1913,7 +1942,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
                                         "being left uninitialized"
                                     }
                                 ),
-                            );
+                            )
+                            .note("this means that this code causes undefined behavior \
+                                when executed")
+                            .help("use `MaybeUninit` instead")
+                            .emit();
                         }
                     }
                 }
diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs
index 40b17651e47..8f9ca8717bd 100644
--- a/src/test/ui/lint/uninitialized-zeroed.rs
+++ b/src/test/ui/lint/uninitialized-zeroed.rs
@@ -6,22 +6,52 @@
 #![allow(deprecated)]
 #![deny(invalid_value)]
 
-use std::mem;
+use std::mem::{self, MaybeUninit};
+
+enum Void {}
+
+struct Ref(&'static i32);
+
+struct Wrap<T> { wrapped: T }
+
+#[allow(unused)]
+fn generic<T: 'static>() {
+    unsafe {
+        let _val: &'static T = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: &'static T = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: Wrap<&'static T> = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: Wrap<&'static T> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+    }
+}
 
 fn main() {
     unsafe {
         let _val: ! = mem::zeroed(); //~ ERROR: does not permit zero-initialization
         let _val: ! = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
 
+        let _val: (i32, !) = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: (i32, !) = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: Void = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: Void = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
         let _val: &'static i32 = mem::zeroed(); //~ ERROR: does not permit zero-initialization
         let _val: &'static i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
 
+        let _val: Ref = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: Ref = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
         let _val: fn() = mem::zeroed(); //~ ERROR: does not permit zero-initialization
         let _val: fn() = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
 
+        let _val: Wrap<fn()> = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: Wrap<fn()> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
         // Some types that should work just fine.
         let _val: Option<&'static i32> = mem::zeroed();
         let _val: Option<fn()> = mem::zeroed();
+        let _val: MaybeUninit<&'static i32> = mem::zeroed();
         let _val: bool = mem::zeroed();
         let _val: i32 = mem::zeroed();
     }
diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr
index c6a47638d38..af54b16bd0b 100644
--- a/src/test/ui/lint/uninitialized-zeroed.stderr
+++ b/src/test/ui/lint/uninitialized-zeroed.stderr
@@ -1,44 +1,169 @@
-error: the type `!` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:15:23
+error: the type `&'static T` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:20:32
    |
-LL |         let _val: ! = mem::zeroed();
-   |                       ^^^^^^^^^^^^^
+LL |         let _val: &'static T = mem::zeroed();
+   |                                ^^^^^^^^^^^^^
    |
 note: lint level defined here
   --> $DIR/uninitialized-zeroed.rs:7:9
    |
 LL | #![deny(invalid_value)]
    |         ^^^^^^^^^^^^^
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `&'static T` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:21:32
+   |
+LL |         let _val: &'static T = mem::uninitialized();
+   |                                ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Wrap<&'static T>` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:23:38
+   |
+LL |         let _val: Wrap<&'static T> = mem::zeroed();
+   |                                      ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Wrap<&'static T>` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:24:38
+   |
+LL |         let _val: Wrap<&'static T> = mem::uninitialized();
+   |                                      ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `!` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:30:23
+   |
+LL |         let _val: ! = mem::zeroed();
+   |                       ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
 
 error: the type `!` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:16:23
+  --> $DIR/uninitialized-zeroed.rs:31:23
    |
 LL |         let _val: ! = mem::uninitialized();
    |                       ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `(i32, !)` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:33:30
+   |
+LL |         let _val: (i32, !) = mem::zeroed();
+   |                              ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `(i32, !)` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:34:30
+   |
+LL |         let _val: (i32, !) = mem::uninitialized();
+   |                              ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Void` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:36:26
+   |
+LL |         let _val: Void = mem::zeroed();
+   |                          ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Void` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:37:26
+   |
+LL |         let _val: Void = mem::uninitialized();
+   |                          ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
 
 error: the type `&'static i32` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:21:34
+  --> $DIR/uninitialized-zeroed.rs:39:34
    |
 LL |         let _val: &'static i32 = mem::zeroed();
    |                                  ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
 
 error: the type `&'static i32` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:22:34
+  --> $DIR/uninitialized-zeroed.rs:40:34
    |
 LL |         let _val: &'static i32 = mem::uninitialized();
    |                                  ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Ref` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:42:25
+   |
+LL |         let _val: Ref = mem::zeroed();
+   |                         ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Ref` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:43:25
+   |
+LL |         let _val: Ref = mem::uninitialized();
+   |                         ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
 
 error: the type `fn()` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:24:26
+  --> $DIR/uninitialized-zeroed.rs:45:26
    |
 LL |         let _val: fn() = mem::zeroed();
    |                          ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
 
 error: the type `fn()` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:25:26
+  --> $DIR/uninitialized-zeroed.rs:46:26
    |
 LL |         let _val: fn() = mem::uninitialized();
    |                          ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Wrap<fn()>` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:48:32
+   |
+LL |         let _val: Wrap<fn()> = mem::zeroed();
+   |                                ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Wrap<fn()>` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:49:32
+   |
+LL |         let _val: Wrap<fn()> = mem::uninitialized();
+   |                                ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
 
-error: aborting due to 6 previous errors
+error: aborting due to 18 previous errors