From ca1e94b131eec4795f1a8dcacdb12f574e7a4a74 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 Aug 2019 08:42:50 +0200 Subject: [PATCH] warn for more cases --- src/librustc_lint/builtin.rs | 43 +++++- src/test/ui/lint/uninitialized-zeroed.rs | 32 +++- src/test/ui/lint/uninitialized-zeroed.stderr | 145 +++++++++++++++++-- 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 { wrapped: T } + +#[allow(unused)] +fn generic() { + 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 = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: Wrap = 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 = 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: aborting due to 6 previous errors +error: the type `Wrap` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:48:32 + | +LL | let _val: Wrap = mem::zeroed(); + | ^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `Wrap` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:49:32 + | +LL | let _val: Wrap = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: aborting due to 18 previous errors