warn for more cases

This commit is contained in:
Ralf Jung 2019-08-07 08:42:50 +02:00
parent da6fbb1895
commit ca1e94b131
3 changed files with 204 additions and 16 deletions

View File

@ -23,7 +23,7 @@
use rustc::hir::def::{Res, DefKind}; use rustc::hir::def::{Res, DefKind};
use rustc::hir::def_id::{DefId, LOCAL_CRATE}; 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 rustc::{lint, util};
use hir::Node; use hir::Node;
use util::nodemap::HirIdSet; 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* /// Return `false` only if we are sure this type does *not*
/// allow zero initialization. /// 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::*; use rustc::ty::TyKind::*;
match ty.sty { match ty.sty {
// Primitive types that don't like 0 as a value. // Primitive types that don't like 0 as a value.
Ref(..) | FnPtr(..) | Never => false, 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. // Conservative fallback.
_ => true, _ => true,
} }
@ -1900,8 +1929,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
// We are extremely conservative with what we warn about. // We are extremely conservative with what we warn about.
let conjured_ty = cx.tables.expr_ty(expr); let conjured_ty = cx.tables.expr_ty(expr);
if !ty_maybe_allows_zero_init(conjured_ty) { if !ty_maybe_allows_zero_init(cx.tcx, conjured_ty) {
cx.span_lint( cx.struct_span_lint(
INVALID_VALUE, INVALID_VALUE,
expr.span, expr.span,
&format!( &format!(
@ -1913,7 +1942,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
"being left uninitialized" "being left uninitialized"
} }
), ),
); )
.note("this means that this code causes undefined behavior \
when executed")
.help("use `MaybeUninit` instead")
.emit();
} }
} }
} }

View File

@ -6,22 +6,52 @@
#![allow(deprecated)] #![allow(deprecated)]
#![deny(invalid_value)] #![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() { fn main() {
unsafe { unsafe {
let _val: ! = mem::zeroed(); //~ ERROR: does not permit zero-initialization let _val: ! = mem::zeroed(); //~ ERROR: does not permit zero-initialization
let _val: ! = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized 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::zeroed(); //~ ERROR: does not permit zero-initialization
let _val: &'static i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized 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::zeroed(); //~ ERROR: does not permit zero-initialization
let _val: fn() = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized 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. // Some types that should work just fine.
let _val: Option<&'static i32> = mem::zeroed(); let _val: Option<&'static i32> = mem::zeroed();
let _val: Option<fn()> = mem::zeroed(); let _val: Option<fn()> = mem::zeroed();
let _val: MaybeUninit<&'static i32> = mem::zeroed();
let _val: bool = mem::zeroed(); let _val: bool = mem::zeroed();
let _val: i32 = mem::zeroed(); let _val: i32 = mem::zeroed();
} }

View File

@ -1,44 +1,169 @@
error: the type `!` does not permit zero-initialization error: the type `&'static T` does not permit zero-initialization
--> $DIR/uninitialized-zeroed.rs:15:23 --> $DIR/uninitialized-zeroed.rs:20:32
| |
LL | let _val: ! = mem::zeroed(); LL | let _val: &'static T = mem::zeroed();
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
| |
note: lint level defined here note: lint level defined here
--> $DIR/uninitialized-zeroed.rs:7:9 --> $DIR/uninitialized-zeroed.rs:7:9
| |
LL | #![deny(invalid_value)] 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 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(); 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 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(); 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 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(); 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 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(); 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 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(); 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<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 18 previous errors