From 5f7716d11bd7df5dbdffdd97a290c671ec1c54d5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 08:39:20 +0200 Subject: [PATCH 01/10] invalid_value: factor finding dangerous inits into separate function --- src/librustc_lint/builtin.rs | 91 +++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 82160080a44d4..afe81b3123d83 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1876,8 +1876,34 @@ declare_lint_pass!(InvalidValue => [INVALID_VALUE]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &hir::Expr) { - const ZEROED_PATH: &[Symbol] = &[sym::core, sym::mem, sym::zeroed]; - const UININIT_PATH: &[Symbol] = &[sym::core, sym::mem, sym::uninitialized]; + #[derive(Debug)] + enum InitKind { Zeroed, Uninit }; + + /// Determine if this expression is a "dangerous initialization". + fn is_dangerous_init(cx: &LateContext<'_, '_>, expr: &hir::Expr) -> Option { + const ZEROED_PATH: &[Symbol] = &[sym::core, sym::mem, sym::zeroed]; + const UININIT_PATH: &[Symbol] = &[sym::core, sym::mem, sym::uninitialized]; + + if let hir::ExprKind::Call(ref path_expr, ref _args) = expr.node { + if let hir::ExprKind::Path(ref qpath) = path_expr.node { + if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id) + .opt_def_id() + { + if cx.match_def_path(def_id, &ZEROED_PATH) { + return Some(InitKind::Zeroed); + } + if cx.match_def_path(def_id, &UININIT_PATH) { + return Some(InitKind::Uninit); + } + // FIXME: Also detect `MaybeUninit::zeroed().assume_init()` and + // `MaybeUninit::uninit().assume_init()`. + // FIXME: Also detect `transmute` from 0. + } + } + } + + None + } /// Information about why a type cannot be initialized this way. /// Contains an error message and optionally a span to point at. @@ -1933,42 +1959,33 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { } } - if let hir::ExprKind::Call(ref path_expr, ref _args) = expr.node { - if let hir::ExprKind::Path(ref qpath) = path_expr.node { - if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() { - if cx.match_def_path(def_id, &ZEROED_PATH) || - cx.match_def_path(def_id, &UININIT_PATH) - { - // This conjures an instance of a type out of nothing, - // using zeroed or uninitialized memory. - // We are extremely conservative with what we warn about. - let conjured_ty = cx.tables.expr_ty(expr); - if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty) { - let mut err = cx.struct_span_lint( - INVALID_VALUE, - expr.span, - &format!( - "the type `{}` does not permit {}", - conjured_ty, - if cx.match_def_path(def_id, &ZEROED_PATH) { - "zero-initialization" - } else { - "being left uninitialized" - } - ), - ); - err.span_label(expr.span, - "this code causes undefined behavior when executed"); - err.span_label(expr.span, "help: use `MaybeUninit` instead"); - if let Some(span) = span { - err.span_note(span, &msg); - } else { - err.note(&msg); - } - err.emit(); - } - } + if let Some(init) = is_dangerous_init(cx, expr) { + // This conjures an instance of a type out of nothing, + // using zeroed or uninitialized memory. + // We are extremely conservative with what we warn about. + let conjured_ty = cx.tables.expr_ty(expr); + if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty) { + let mut err = cx.struct_span_lint( + INVALID_VALUE, + expr.span, + &format!( + "the type `{}` does not permit {}", + conjured_ty, + match init { + InitKind::Zeroed => "zero-initialization", + InitKind::Uninit => "being left uninitialized", + }, + ), + ); + err.span_label(expr.span, + "this code causes undefined behavior when executed"); + err.span_label(expr.span, "help: use `MaybeUninit` instead"); + if let Some(span) = span { + err.span_note(span, &msg); + } else { + err.note(&msg); } + err.emit(); } } } From 25d8a0a3514c3d8dd8362d0eb68272dc50168af3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 09:39:25 +0200 Subject: [PATCH 02/10] warn about uninit bools and chars --- src/librustc_lint/builtin.rs | 21 ++++++++++----- src/test/ui/lint/uninitialized-zeroed.rs | 3 +++ src/test/ui/lint/uninitialized-zeroed.stderr | 28 +++++++++++++++++++- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index afe81b3123d83..14bd9a381f0b3 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1876,7 +1876,7 @@ declare_lint_pass!(InvalidValue => [INVALID_VALUE]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &hir::Expr) { - #[derive(Debug)] + #[derive(Debug, Copy, Clone, PartialEq)] enum InitKind { Zeroed, Uninit }; /// Determine if this expression is a "dangerous initialization". @@ -1911,7 +1911,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { /// Return `Some` only if we are sure this type does *not* /// allow zero initialization. - fn ty_find_init_error<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + fn ty_find_init_error<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + init: InitKind, + ) -> Option { use rustc::ty::TyKind::*; match ty.sty { // Primitive types that don't like 0 as a value. @@ -1919,6 +1923,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { Adt(..) if ty.is_box() => Some((format!("`Box` must be non-null"), None)), FnPtr(..) => Some((format!("Function pointers must be non-null"), None)), Never => Some((format!("The never type (`!`) has no valid value"), None)), + // Primitive types with other constraints + Bool if init == InitKind::Uninit => + Some((format!("Booleans must be `true` or `false`"), None)), + Char if init == InitKind::Uninit => + Some((format!("Characters must be a valid unicode codepoint"), None)), // Recurse for some compound types. Adt(adt_def, substs) if !adt_def.is_union() => { match adt_def.variants.len() { @@ -1931,6 +1940,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { ty_find_init_error( tcx, field.ty(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. @@ -1949,11 +1959,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { } Tuple(..) => { // Proceed recursively, check all fields. - ty.tuple_fields().find_map(|field| ty_find_init_error(tcx, field)) + ty.tuple_fields().find_map(|field| ty_find_init_error(tcx, field, init)) } // FIXME: Would be nice to also warn for `NonNull`/`NonZero*`. - // FIXME: *Only for `mem::uninitialized`*, we could also warn for `bool`, - // `char`, and any multivariant enum. + // FIXME: *Only for `mem::uninitialized`*, we could also warn for multivariant enum. // Conservative fallback. _ => None, } @@ -1964,7 +1973,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { // using zeroed or uninitialized memory. // We are extremely conservative with what we warn about. let conjured_ty = cx.tables.expr_ty(expr); - if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty) { + if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty, init) { let mut err = cx.struct_span_lint( INVALID_VALUE, expr.span, diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs index d816479bbbbe3..aafc8507af616 100644 --- a/src/test/ui/lint/uninitialized-zeroed.rs +++ b/src/test/ui/lint/uninitialized-zeroed.rs @@ -56,6 +56,9 @@ fn main() { let _val: Wrap<(RefPair, i32)> = mem::zeroed(); //~ ERROR: does not permit zero-initialization let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: bool = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + 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(); diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index 1b15fc2152559..d8a4bf0b04953 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -285,5 +285,31 @@ note: References must be non-null (in this struct field) LL | struct RefPair((&'static i32, i32)); | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 22 previous errors +error: the type `bool` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:59:26 + | +LL | let _val: bool = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | + = note: Booleans must be `true` or `false` + +error: the type `Wrap` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:60:32 + | +LL | let _val: Wrap = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | +note: Characters must be a valid unicode codepoint (in this struct field) + --> $DIR/uninitialized-zeroed.rs:16:18 + | +LL | struct Wrap { wrapped: T } + | ^^^^^^^^^^ + +error: aborting due to 24 previous errors From 0d242b3f9098fe13518da4442692f29915cf2da4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 10:13:47 +0200 Subject: [PATCH 03/10] invalid_value: warn for types with custom valid range --- src/librustc_lint/builtin.rs | 20 ++++- src/test/ui/lint/uninitialized-zeroed.rs | 21 ++++- src/test/ui/lint/uninitialized-zeroed.stderr | 91 ++++++++++++++------ 3 files changed, 102 insertions(+), 30 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 14bd9a381f0b3..f4f6bd37774c7 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1928,8 +1928,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { Some((format!("Booleans must be `true` or `false`"), None)), Char if init == InitKind::Uninit => Some((format!("Characters must be a valid unicode codepoint"), None)), - // Recurse for some compound types. + // 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). + use std::ops::Bound; + match tcx.layout_scalar_valid_range(adt_def.did) { + // We exploit here that `layout_scalar_valid_range` will never + // return `Bound::Excluded`. (And we have tests checking that we + // handle the attribute correctly.) + (Bound::Included(lo), _) if lo > 0 => + return Some((format!("{} must be non-null", ty), None)), + (Bound::Included(_), _) | (_, Bound::Included(_)) + if init == InitKind::Uninit => + return Some(( + format!("{} must be initialized inside its custom valid range", ty), + None, + )), + _ => {} + } + // Now, recurse. match adt_def.variants.len() { 0 => Some((format!("0-variant enums have no valid value"), None)), 1 => { @@ -1961,7 +1978,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { // Proceed recursively, check all fields. ty.tuple_fields().find_map(|field| ty_find_init_error(tcx, field, init)) } - // FIXME: Would be nice to also warn for `NonNull`/`NonZero*`. // FIXME: *Only for `mem::uninitialized`*, we could also warn for multivariant enum. // Conservative fallback. _ => None, diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs index aafc8507af616..237f2c0141dce 100644 --- a/src/test/ui/lint/uninitialized-zeroed.rs +++ b/src/test/ui/lint/uninitialized-zeroed.rs @@ -2,7 +2,7 @@ // This test checks that calling `mem::{uninitialized,zeroed}` with certain types results // in a lint. -#![feature(never_type)] +#![feature(never_type, rustc_attrs)] #![allow(deprecated)] #![deny(invalid_value)] @@ -16,6 +16,11 @@ struct RefPair((&'static i32, i32)); struct Wrap { wrapped: T } enum WrapEnum { Wrapped(T) } +#[rustc_layout_scalar_valid_range_start(0)] +#[rustc_layout_scalar_valid_range_end(128)] +#[repr(transparent)] +pub(crate) struct NonBig(u64); + #[allow(unused)] fn generic() { unsafe { @@ -29,6 +34,7 @@ fn generic() { fn main() { unsafe { + // Things that cannot even be zero. let _val: ! = mem::zeroed(); //~ ERROR: does not permit zero-initialization let _val: ! = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized @@ -56,14 +62,23 @@ fn main() { let _val: Wrap<(RefPair, i32)> = mem::zeroed(); //~ ERROR: does not permit zero-initialization let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: Vec = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: Vec = 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 + + let _val: Wrap = mem::zeroed(); let _val: Wrap = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized - // Some types that should work just fine. + let _val: NonBig = mem::zeroed(); + let _val: NonBig = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + + // Some more 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 d8a4bf0b04953..c5006345e52f1 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -1,5 +1,5 @@ error: the type `&'static T` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:22:32 + --> $DIR/uninitialized-zeroed.rs:27: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 `&'static T` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:23:32 + --> $DIR/uninitialized-zeroed.rs:28: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<&'static T>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:25:38 + --> $DIR/uninitialized-zeroed.rs:30:38 | LL | let _val: Wrap<&'static T> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `Wrap<&'static T>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:26:38 + --> $DIR/uninitialized-zeroed.rs:31: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:32:23 + --> $DIR/uninitialized-zeroed.rs:38:23 | LL | let _val: ! = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | let _val: ! = mem::zeroed(); = note: The never type (`!`) has no valid value error: the type `!` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:33:23 + --> $DIR/uninitialized-zeroed.rs:39:23 | LL | let _val: ! = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -78,7 +78,7 @@ LL | let _val: ! = mem::uninitialized(); = note: The never type (`!`) has no valid value error: the type `(i32, !)` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:35:30 + --> $DIR/uninitialized-zeroed.rs:41:30 | LL | let _val: (i32, !) = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _val: (i32, !) = mem::zeroed(); = note: The never type (`!`) has no valid value error: the type `(i32, !)` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:36:30 + --> $DIR/uninitialized-zeroed.rs:42:30 | LL | let _val: (i32, !) = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | let _val: (i32, !) = mem::uninitialized(); = note: The never type (`!`) has no valid value error: the type `Void` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:38:26 + --> $DIR/uninitialized-zeroed.rs:44:26 | LL | let _val: Void = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL | let _val: Void = mem::zeroed(); = note: 0-variant enums have no valid value error: the type `Void` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:39:26 + --> $DIR/uninitialized-zeroed.rs:45:26 | LL | let _val: Void = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL | let _val: Void = mem::uninitialized(); = note: 0-variant enums have no valid value error: the type `&'static i32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:41:34 + --> $DIR/uninitialized-zeroed.rs:47: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 `&'static i32` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:42:34 + --> $DIR/uninitialized-zeroed.rs:48: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:44:25 + --> $DIR/uninitialized-zeroed.rs:50: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:45:25 + --> $DIR/uninitialized-zeroed.rs:51: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:47:26 + --> $DIR/uninitialized-zeroed.rs:53: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:48:26 + --> $DIR/uninitialized-zeroed.rs:54: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:50:32 + --> $DIR/uninitialized-zeroed.rs:56: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:51:32 + --> $DIR/uninitialized-zeroed.rs:57: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:53:36 + --> $DIR/uninitialized-zeroed.rs:59: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:54:36 + --> $DIR/uninitialized-zeroed.rs:60: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:56:42 + --> $DIR/uninitialized-zeroed.rs:62: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:57:42 + --> $DIR/uninitialized-zeroed.rs:63:42 | LL | let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -285,8 +285,38 @@ note: References must be non-null (in this struct field) LL | struct RefPair((&'static i32, i32)); | ^^^^^^^^^^^^^^^^^^^ +error: the type `std::vec::Vec` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:65:30 + | +LL | let _val: Vec = mem::zeroed(); + | ^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | +note: std::ptr::Unique must be non-null (in this struct field) + --> $SRC_DIR/liballoc/raw_vec.rs:LL:COL + | +LL | ptr: Unique, + | ^^^^^^^^^^^^^^ + +error: the type `std::vec::Vec` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:66:30 + | +LL | let _val: Vec = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | +note: std::ptr::Unique must be non-null (in this struct field) + --> $SRC_DIR/liballoc/raw_vec.rs:LL:COL + | +LL | ptr: Unique, + | ^^^^^^^^^^^^^^ + error: the type `bool` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:59:26 + --> $DIR/uninitialized-zeroed.rs:70:26 | LL | let _val: bool = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -297,7 +327,7 @@ LL | let _val: bool = mem::uninitialized(); = note: Booleans must be `true` or `false` error: the type `Wrap` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:60:32 + --> $DIR/uninitialized-zeroed.rs:73:32 | LL | let _val: Wrap = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -311,5 +341,16 @@ note: Characters must be a valid unicode codepoint (in this struct field) LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ -error: aborting due to 24 previous errors +error: the type `NonBig` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:76:28 + | +LL | let _val: NonBig = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | + = note: NonBig must be initialized inside its custom valid range + +error: aborting due to 27 previous errors From 9ab1d5c73a67e0936f71abe6eaccef8d0b4bd25e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 10:20:18 +0200 Subject: [PATCH 04/10] multi-variant enums are tricky --- src/librustc_lint/builtin.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index f4f6bd37774c7..74cff1bab0f81 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1971,6 +1971,9 @@ impl<'a, 'tcx> LateLintPass<'a, '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. } } @@ -1978,7 +1981,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { // Proceed recursively, check all fields. ty.tuple_fields().find_map(|field| ty_find_init_error(tcx, field, init)) } - // FIXME: *Only for `mem::uninitialized`*, we could also warn for multivariant enum. // Conservative fallback. _ => None, } From a9efa738ab4e9517a86314f5c0787efef7d8e2a9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 11:48:30 +0200 Subject: [PATCH 05/10] invalid_value: also detect transmute-from-0 (seen in the wild) --- src/librustc_lint/builtin.rs | 38 ++++++- src/test/ui/lint/uninitialized-zeroed.rs | 6 + src/test/ui/lint/uninitialized-zeroed.stderr | 111 ++++++++++++------- 3 files changed, 110 insertions(+), 45 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 74cff1bab0f81..877dc43fb7b9b 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1879,12 +1879,38 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { #[derive(Debug, Copy, Clone, PartialEq)] enum InitKind { Zeroed, Uninit }; + /// Information about why a type cannot be initialized this way. + /// Contains an error message and optionally a span to point at. + type InitError = (String, Option); + + /// Test if this constant is all-0. + fn is_zero(expr: &hir::Expr) -> bool { + use hir::ExprKind::*; + use syntax::ast::LitKind::*; + match &expr.node { + Lit(lit) => + if let Int(i, _) = lit.node { + i == 0 + } else { + false + }, + Tup(tup) => + tup.iter().all(is_zero), + _ => + false + } + } + /// Determine if this expression is a "dangerous initialization". fn is_dangerous_init(cx: &LateContext<'_, '_>, expr: &hir::Expr) -> Option { const ZEROED_PATH: &[Symbol] = &[sym::core, sym::mem, sym::zeroed]; const UININIT_PATH: &[Symbol] = &[sym::core, sym::mem, sym::uninitialized]; + // `transmute` is inside an anonymous module (the `extern` block?); + // `Invalid` represents the empty string and matches that. + const TRANSMUTE_PATH: &[Symbol] = + &[sym::core, sym::intrinsics, kw::Invalid, sym::transmute]; - if let hir::ExprKind::Call(ref path_expr, ref _args) = expr.node { + if let hir::ExprKind::Call(ref path_expr, ref args) = expr.node { if let hir::ExprKind::Path(ref qpath) = path_expr.node { if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id) .opt_def_id() @@ -1895,9 +1921,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { if cx.match_def_path(def_id, &UININIT_PATH) { return Some(InitKind::Uninit); } + if cx.match_def_path(def_id, &TRANSMUTE_PATH) { + if is_zero(&args[0]) { + return Some(InitKind::Zeroed); + } + } // FIXME: Also detect `MaybeUninit::zeroed().assume_init()` and // `MaybeUninit::uninit().assume_init()`. - // FIXME: Also detect `transmute` from 0. } } } @@ -1905,10 +1935,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { None } - /// Information about why a type cannot be initialized this way. - /// Contains an error message and optionally a span to point at. - type InitError = (String, Option); - /// Return `Some` only if we are sure this type does *not* /// allow zero initialization. fn ty_find_init_error<'tcx>( diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs index 237f2c0141dce..54e1210768cd2 100644 --- a/src/test/ui/lint/uninitialized-zeroed.rs +++ b/src/test/ui/lint/uninitialized-zeroed.rs @@ -7,6 +7,7 @@ #![deny(invalid_value)] use std::mem::{self, MaybeUninit}; +use std::num::NonZeroU32; enum Void {} @@ -75,6 +76,11 @@ fn main() { let _val: NonBig = mem::zeroed(); let _val: NonBig = 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 + let _val: NonZeroU32 = mem::transmute(0); //~ ERROR: does not permit zero-initialization + // Some more types that should work just fine. let _val: Option<&'static i32> = mem::zeroed(); let _val: Option = mem::zeroed(); diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index c5006345e52f1..90a51ba57e96f 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -1,5 +1,5 @@ error: the type `&'static T` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:27:32 + --> $DIR/uninitialized-zeroed.rs:28: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 `&'static T` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:28:32 + --> $DIR/uninitialized-zeroed.rs:29: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<&'static T>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:30:38 + --> $DIR/uninitialized-zeroed.rs:31:38 | LL | let _val: Wrap<&'static T> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -35,13 +35,13 @@ LL | let _val: Wrap<&'static T> = mem::zeroed(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:16:18 + --> $DIR/uninitialized-zeroed.rs:17:18 | LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `Wrap<&'static T>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:31:38 + --> $DIR/uninitialized-zeroed.rs:32:38 | LL | let _val: Wrap<&'static T> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -50,13 +50,13 @@ LL | let _val: Wrap<&'static T> = mem::uninitialized(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:16:18 + --> $DIR/uninitialized-zeroed.rs:17:18 | LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `!` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:38:23 + --> $DIR/uninitialized-zeroed.rs:39:23 | LL | let _val: ! = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | let _val: ! = mem::zeroed(); = note: The never type (`!`) has no valid value error: the type `!` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:39:23 + --> $DIR/uninitialized-zeroed.rs:40:23 | LL | let _val: ! = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -78,7 +78,7 @@ LL | let _val: ! = mem::uninitialized(); = note: The never type (`!`) has no valid value error: the type `(i32, !)` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:41:30 + --> $DIR/uninitialized-zeroed.rs:42:30 | LL | let _val: (i32, !) = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _val: (i32, !) = mem::zeroed(); = note: The never type (`!`) has no valid value error: the type `(i32, !)` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:42:30 + --> $DIR/uninitialized-zeroed.rs:43:30 | LL | let _val: (i32, !) = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | let _val: (i32, !) = mem::uninitialized(); = note: The never type (`!`) has no valid value error: the type `Void` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:44:26 + --> $DIR/uninitialized-zeroed.rs:45:26 | LL | let _val: Void = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL | let _val: Void = mem::zeroed(); = note: 0-variant enums have no valid value error: the type `Void` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:45:26 + --> $DIR/uninitialized-zeroed.rs:46:26 | LL | let _val: Void = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL | let _val: Void = mem::uninitialized(); = note: 0-variant enums have no valid value error: the type `&'static i32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:47:34 + --> $DIR/uninitialized-zeroed.rs:48: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 `&'static i32` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:48:34 + --> $DIR/uninitialized-zeroed.rs:49: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:50:25 + --> $DIR/uninitialized-zeroed.rs:51:25 | LL | let _val: Ref = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -153,13 +153,13 @@ LL | let _val: Ref = mem::zeroed(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:13:12 + --> $DIR/uninitialized-zeroed.rs:14:12 | LL | struct Ref(&'static i32); | ^^^^^^^^^^^^ error: the type `Ref` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:51:25 + --> $DIR/uninitialized-zeroed.rs:52:25 | LL | let _val: Ref = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -168,13 +168,13 @@ LL | let _val: Ref = mem::uninitialized(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:13:12 + --> $DIR/uninitialized-zeroed.rs:14:12 | LL | struct Ref(&'static i32); | ^^^^^^^^^^^^ error: the type `fn()` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:53:26 + --> $DIR/uninitialized-zeroed.rs:54: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:54:26 + --> $DIR/uninitialized-zeroed.rs:55: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:56:32 + --> $DIR/uninitialized-zeroed.rs:57:32 | LL | let _val: Wrap = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -205,13 +205,13 @@ LL | let _val: Wrap = mem::zeroed(); | help: use `MaybeUninit` instead | note: Function pointers must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:16:18 + --> $DIR/uninitialized-zeroed.rs:17:18 | LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `Wrap` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:57:32 + --> $DIR/uninitialized-zeroed.rs:58:32 | LL | let _val: Wrap = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -220,13 +220,13 @@ LL | let _val: Wrap = mem::uninitialized(); | help: use `MaybeUninit` instead | note: Function pointers must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:16:18 + --> $DIR/uninitialized-zeroed.rs:17:18 | LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `WrapEnum` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:59:36 + --> $DIR/uninitialized-zeroed.rs:60:36 | LL | let _val: WrapEnum = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -235,13 +235,13 @@ LL | let _val: WrapEnum = mem::zeroed(); | help: use `MaybeUninit` instead | note: Function pointers must be non-null (in this enum field) - --> $DIR/uninitialized-zeroed.rs:17:28 + --> $DIR/uninitialized-zeroed.rs:18:28 | LL | enum WrapEnum { Wrapped(T) } | ^ error: the type `WrapEnum` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:60:36 + --> $DIR/uninitialized-zeroed.rs:61:36 | LL | let _val: WrapEnum = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -250,13 +250,13 @@ LL | let _val: WrapEnum = mem::uninitialized(); | help: use `MaybeUninit` instead | note: Function pointers must be non-null (in this enum field) - --> $DIR/uninitialized-zeroed.rs:17:28 + --> $DIR/uninitialized-zeroed.rs:18:28 | LL | enum WrapEnum { Wrapped(T) } | ^ error: the type `Wrap<(RefPair, i32)>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:62:42 + --> $DIR/uninitialized-zeroed.rs:63:42 | LL | let _val: Wrap<(RefPair, i32)> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -265,13 +265,13 @@ LL | let _val: Wrap<(RefPair, i32)> = mem::zeroed(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:14:16 + --> $DIR/uninitialized-zeroed.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:63:42 + --> $DIR/uninitialized-zeroed.rs:64:42 | LL | let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -280,13 +280,13 @@ LL | let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:14:16 + --> $DIR/uninitialized-zeroed.rs:15:16 | LL | struct RefPair((&'static i32, i32)); | ^^^^^^^^^^^^^^^^^^^ error: the type `std::vec::Vec` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:65:30 + --> $DIR/uninitialized-zeroed.rs:66:30 | LL | let _val: Vec = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -301,7 +301,7 @@ LL | ptr: Unique, | ^^^^^^^^^^^^^^ error: the type `std::vec::Vec` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:66:30 + --> $DIR/uninitialized-zeroed.rs:67:30 | LL | let _val: Vec = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -316,7 +316,7 @@ LL | ptr: Unique, | ^^^^^^^^^^^^^^ error: the type `bool` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:70:26 + --> $DIR/uninitialized-zeroed.rs:71:26 | LL | let _val: bool = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -327,7 +327,7 @@ LL | let _val: bool = mem::uninitialized(); = note: Booleans must be `true` or `false` error: the type `Wrap` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:73:32 + --> $DIR/uninitialized-zeroed.rs:74:32 | LL | let _val: Wrap = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -336,13 +336,13 @@ LL | let _val: Wrap = mem::uninitialized(); | help: use `MaybeUninit` instead | note: Characters must be a valid unicode codepoint (in this struct field) - --> $DIR/uninitialized-zeroed.rs:16:18 + --> $DIR/uninitialized-zeroed.rs:17:18 | LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `NonBig` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:76:28 + --> $DIR/uninitialized-zeroed.rs:77:28 | LL | let _val: NonBig = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -352,5 +352,38 @@ LL | let _val: NonBig = mem::uninitialized(); | = note: NonBig must be initialized inside its custom valid range -error: aborting due to 27 previous errors +error: the type `&'static i32` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:80:34 + | +LL | let _val: &'static i32 = mem::transmute(0usize); + | ^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | + = note: References must be non-null + +error: the type `&'static [i32]` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:81:36 + | +LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | + = note: References must be non-null + +error: the type `std::num::NonZeroU32` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:82:32 + | +LL | let _val: NonZeroU32 = mem::transmute(0); + | ^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | + = note: std::num::NonZeroU32 must be non-null + +error: aborting due to 30 previous errors From 4821663a2bd2ef6c8fbc9c3f24a00371e71d419a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 13:42:03 +0200 Subject: [PATCH 06/10] Full stop Co-Authored-By: Mazdak Farrokhzad --- src/librustc_lint/builtin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 877dc43fb7b9b..3279f2436220c 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1949,7 +1949,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { Adt(..) if ty.is_box() => Some((format!("`Box` must be non-null"), None)), FnPtr(..) => Some((format!("Function pointers must be non-null"), None)), Never => Some((format!("The never type (`!`) has no valid value"), None)), - // Primitive types with other constraints + // Primitive types with other constraints. Bool if init == InitKind::Uninit => Some((format!("Booleans must be `true` or `false`"), None)), Char if init == InitKind::Uninit => From 689c210b472d4469bec3fb62da7704989d8305fd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 13:45:11 +0200 Subject: [PATCH 07/10] fix tests --- src/test/ui/consts/const-eval/ub-nonnull.rs | 2 +- src/test/ui/consts/const-eval/ub-ref.rs | 2 +- src/test/ui/consts/const-eval/ub-upvars.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/ui/consts/const-eval/ub-nonnull.rs b/src/test/ui/consts/const-eval/ub-nonnull.rs index 431ff356ade19..9edae1965ce16 100644 --- a/src/test/ui/consts/const-eval/ub-nonnull.rs +++ b/src/test/ui/consts/const-eval/ub-nonnull.rs @@ -1,5 +1,5 @@ #![feature(rustc_attrs, const_transmute)] -#![allow(const_err)] // make sure we cannot allow away the errors tested here +#![allow(const_err, invalid_value)] // make sure we cannot allow away the errors tested here use std::mem; use std::ptr::NonNull; diff --git a/src/test/ui/consts/const-eval/ub-ref.rs b/src/test/ui/consts/const-eval/ub-ref.rs index 0d8f30159b316..bbab85c2121a5 100644 --- a/src/test/ui/consts/const-eval/ub-ref.rs +++ b/src/test/ui/consts/const-eval/ub-ref.rs @@ -1,6 +1,6 @@ // ignore-tidy-linelength #![feature(const_transmute)] -#![allow(const_err)] // make sure we cannot allow away the errors tested here +#![allow(const_err, invalid_value)] // make sure we cannot allow away the errors tested here use std::mem; diff --git a/src/test/ui/consts/const-eval/ub-upvars.rs b/src/test/ui/consts/const-eval/ub-upvars.rs index 0a427cd8857e8..baab14dc16141 100644 --- a/src/test/ui/consts/const-eval/ub-upvars.rs +++ b/src/test/ui/consts/const-eval/ub-upvars.rs @@ -1,5 +1,5 @@ #![feature(const_transmute)] -#![allow(const_err)] // make sure we cannot allow away the errors tested here +#![allow(const_err, invalid_value)] // make sure we cannot allow away the errors tested here use std::mem; From f19087dd7cdbac5f11ffc606b08c489ea730949a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 13:49:27 +0200 Subject: [PATCH 08/10] drift leftward --- src/librustc_lint/builtin.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 3279f2436220c..94a16e02e20b6 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1912,23 +1912,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { if let hir::ExprKind::Call(ref path_expr, ref args) = expr.node { if let hir::ExprKind::Path(ref qpath) = path_expr.node { - if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id) - .opt_def_id() - { - if cx.match_def_path(def_id, &ZEROED_PATH) { + let def_id = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; + + if cx.match_def_path(def_id, &ZEROED_PATH) { + return Some(InitKind::Zeroed); + } + if cx.match_def_path(def_id, &UININIT_PATH) { + return Some(InitKind::Uninit); + } + if cx.match_def_path(def_id, &TRANSMUTE_PATH) { + if is_zero(&args[0]) { return Some(InitKind::Zeroed); } - if cx.match_def_path(def_id, &UININIT_PATH) { - return Some(InitKind::Uninit); - } - if cx.match_def_path(def_id, &TRANSMUTE_PATH) { - if is_zero(&args[0]) { - return Some(InitKind::Zeroed); - } - } - // FIXME: Also detect `MaybeUninit::zeroed().assume_init()` and - // `MaybeUninit::uninit().assume_init()`. } + // FIXME: Also detect `MaybeUninit::zeroed().assume_init()` and + // `MaybeUninit::uninit().assume_init()`. } } From 72d9fe8b0e4940b4314f190dd14235fa83046338 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 16:48:08 +0200 Subject: [PATCH 09/10] less & --- src/librustc_lint/builtin.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 94a16e02e20b6..ce7681c974a5d 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1914,13 +1914,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { if let hir::ExprKind::Path(ref qpath) = path_expr.node { let def_id = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; - if cx.match_def_path(def_id, &ZEROED_PATH) { + if cx.match_def_path(def_id, ZEROED_PATH) { return Some(InitKind::Zeroed); } - if cx.match_def_path(def_id, &UININIT_PATH) { + if cx.match_def_path(def_id, UININIT_PATH) { return Some(InitKind::Uninit); } - if cx.match_def_path(def_id, &TRANSMUTE_PATH) { + if cx.match_def_path(def_id, TRANSMUTE_PATH) { if is_zero(&args[0]) { return Some(InitKind::Zeroed); } From 3288be515feb4e2143c97ccf0e8455f572e3b360 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 22:11:43 +0200 Subject: [PATCH 10/10] test in a way that works even with musl --- src/test/ui/lint/uninitialized-zeroed.rs | 5 +- src/test/ui/lint/uninitialized-zeroed.stderr | 118 +++++++++---------- 2 files changed, 58 insertions(+), 65 deletions(-) diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs index 54e1210768cd2..5cf62b8691239 100644 --- a/src/test/ui/lint/uninitialized-zeroed.rs +++ b/src/test/ui/lint/uninitialized-zeroed.rs @@ -7,6 +7,7 @@ #![deny(invalid_value)] use std::mem::{self, MaybeUninit}; +use std::ptr::NonNull; use std::num::NonZeroU32; enum Void {} @@ -63,8 +64,8 @@ fn main() { let _val: Wrap<(RefPair, i32)> = mem::zeroed(); //~ ERROR: does not permit zero-initialization let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized - let _val: Vec = mem::zeroed(); //~ ERROR: does not permit zero-initialization - let _val: Vec = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: NonNull = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: NonNull = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized // Things that can be zero, but not uninit. let _val: bool = mem::zeroed(); diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index 90a51ba57e96f..a36a32a39a11b 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -1,5 +1,5 @@ error: the type `&'static T` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:28:32 + --> $DIR/uninitialized-zeroed.rs:29: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 `&'static T` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:29:32 + --> $DIR/uninitialized-zeroed.rs:30: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<&'static T>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:31:38 + --> $DIR/uninitialized-zeroed.rs:32:38 | LL | let _val: Wrap<&'static T> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -35,13 +35,13 @@ LL | let _val: Wrap<&'static T> = mem::zeroed(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:17:18 + --> $DIR/uninitialized-zeroed.rs:18:18 | LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `Wrap<&'static T>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:32:38 + --> $DIR/uninitialized-zeroed.rs:33:38 | LL | let _val: Wrap<&'static T> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -50,13 +50,13 @@ LL | let _val: Wrap<&'static T> = mem::uninitialized(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:17:18 + --> $DIR/uninitialized-zeroed.rs:18:18 | LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `!` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:39:23 + --> $DIR/uninitialized-zeroed.rs:40:23 | LL | let _val: ! = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | let _val: ! = mem::zeroed(); = note: The never type (`!`) has no valid value error: the type `!` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:40:23 + --> $DIR/uninitialized-zeroed.rs:41:23 | LL | let _val: ! = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -78,7 +78,7 @@ LL | let _val: ! = mem::uninitialized(); = note: The never type (`!`) has no valid value error: the type `(i32, !)` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:42:30 + --> $DIR/uninitialized-zeroed.rs:43:30 | LL | let _val: (i32, !) = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _val: (i32, !) = mem::zeroed(); = note: The never type (`!`) has no valid value error: the type `(i32, !)` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:43:30 + --> $DIR/uninitialized-zeroed.rs:44:30 | LL | let _val: (i32, !) = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | let _val: (i32, !) = mem::uninitialized(); = note: The never type (`!`) has no valid value error: the type `Void` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:45:26 + --> $DIR/uninitialized-zeroed.rs:46:26 | LL | let _val: Void = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL | let _val: Void = mem::zeroed(); = note: 0-variant enums have no valid value error: the type `Void` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:46:26 + --> $DIR/uninitialized-zeroed.rs:47:26 | LL | let _val: Void = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL | let _val: Void = mem::uninitialized(); = note: 0-variant enums have no valid value error: the type `&'static i32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:48:34 + --> $DIR/uninitialized-zeroed.rs:49: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 `&'static i32` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:49:34 + --> $DIR/uninitialized-zeroed.rs:50: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:51:25 + --> $DIR/uninitialized-zeroed.rs:52:25 | LL | let _val: Ref = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -153,13 +153,13 @@ LL | let _val: Ref = mem::zeroed(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:14:12 + --> $DIR/uninitialized-zeroed.rs:15:12 | LL | struct Ref(&'static i32); | ^^^^^^^^^^^^ error: the type `Ref` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:52:25 + --> $DIR/uninitialized-zeroed.rs:53:25 | LL | let _val: Ref = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -168,13 +168,13 @@ LL | let _val: Ref = mem::uninitialized(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:14:12 + --> $DIR/uninitialized-zeroed.rs:15:12 | LL | struct Ref(&'static i32); | ^^^^^^^^^^^^ error: the type `fn()` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:54:26 + --> $DIR/uninitialized-zeroed.rs:55: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:55:26 + --> $DIR/uninitialized-zeroed.rs:56: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:57:32 + --> $DIR/uninitialized-zeroed.rs:58:32 | LL | let _val: Wrap = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -205,13 +205,13 @@ LL | let _val: Wrap = mem::zeroed(); | help: use `MaybeUninit` instead | note: Function pointers must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:17:18 + --> $DIR/uninitialized-zeroed.rs:18:18 | LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `Wrap` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:58:32 + --> $DIR/uninitialized-zeroed.rs:59:32 | LL | let _val: Wrap = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -220,13 +220,13 @@ LL | let _val: Wrap = mem::uninitialized(); | help: use `MaybeUninit` instead | note: Function pointers must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:17:18 + --> $DIR/uninitialized-zeroed.rs:18:18 | LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `WrapEnum` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:60:36 + --> $DIR/uninitialized-zeroed.rs:61:36 | LL | let _val: WrapEnum = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -235,13 +235,13 @@ LL | let _val: WrapEnum = mem::zeroed(); | help: use `MaybeUninit` instead | note: Function pointers must be non-null (in this enum field) - --> $DIR/uninitialized-zeroed.rs:18:28 + --> $DIR/uninitialized-zeroed.rs:19:28 | LL | enum WrapEnum { Wrapped(T) } | ^ error: the type `WrapEnum` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:61:36 + --> $DIR/uninitialized-zeroed.rs:62:36 | LL | let _val: WrapEnum = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -250,13 +250,13 @@ LL | let _val: WrapEnum = mem::uninitialized(); | help: use `MaybeUninit` instead | note: Function pointers must be non-null (in this enum field) - --> $DIR/uninitialized-zeroed.rs:18:28 + --> $DIR/uninitialized-zeroed.rs:19:28 | LL | enum WrapEnum { Wrapped(T) } | ^ error: the type `Wrap<(RefPair, i32)>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:63:42 + --> $DIR/uninitialized-zeroed.rs:64:42 | LL | let _val: Wrap<(RefPair, i32)> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -265,13 +265,13 @@ LL | let _val: Wrap<(RefPair, i32)> = mem::zeroed(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:15:16 + --> $DIR/uninitialized-zeroed.rs:16:16 | LL | struct RefPair((&'static i32, i32)); | ^^^^^^^^^^^^^^^^^^^ error: the type `Wrap<(RefPair, i32)>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:64:42 + --> $DIR/uninitialized-zeroed.rs:65:42 | LL | let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -280,43 +280,35 @@ LL | let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); | help: use `MaybeUninit` instead | note: References must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:15:16 + --> $DIR/uninitialized-zeroed.rs:16:16 | LL | struct RefPair((&'static i32, i32)); | ^^^^^^^^^^^^^^^^^^^ -error: the type `std::vec::Vec` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:66:30 - | -LL | let _val: Vec = mem::zeroed(); - | ^^^^^^^^^^^^^ - | | - | this code causes undefined behavior when executed - | help: use `MaybeUninit` instead +error: the type `std::ptr::NonNull` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:67:34 | -note: std::ptr::Unique must be non-null (in this struct field) - --> $SRC_DIR/liballoc/raw_vec.rs:LL:COL +LL | let _val: NonNull = mem::zeroed(); + | ^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead | -LL | ptr: Unique, - | ^^^^^^^^^^^^^^ + = note: std::ptr::NonNull must be non-null -error: the type `std::vec::Vec` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:67:30 - | -LL | let _val: Vec = mem::uninitialized(); - | ^^^^^^^^^^^^^^^^^^^^ - | | - | this code causes undefined behavior when executed - | help: use `MaybeUninit` instead +error: the type `std::ptr::NonNull` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:68:34 | -note: std::ptr::Unique must be non-null (in this struct field) - --> $SRC_DIR/liballoc/raw_vec.rs:LL:COL +LL | let _val: NonNull = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead | -LL | ptr: Unique, - | ^^^^^^^^^^^^^^ + = note: std::ptr::NonNull must be non-null error: the type `bool` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:71:26 + --> $DIR/uninitialized-zeroed.rs:72:26 | LL | let _val: bool = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -327,7 +319,7 @@ LL | let _val: bool = mem::uninitialized(); = note: Booleans must be `true` or `false` error: the type `Wrap` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:74:32 + --> $DIR/uninitialized-zeroed.rs:75:32 | LL | let _val: Wrap = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -336,13 +328,13 @@ LL | let _val: Wrap = mem::uninitialized(); | help: use `MaybeUninit` instead | note: Characters must be a valid unicode codepoint (in this struct field) - --> $DIR/uninitialized-zeroed.rs:17:18 + --> $DIR/uninitialized-zeroed.rs:18:18 | LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `NonBig` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:77:28 + --> $DIR/uninitialized-zeroed.rs:78:28 | LL | let _val: NonBig = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -353,7 +345,7 @@ LL | let _val: NonBig = mem::uninitialized(); = note: NonBig must be initialized inside its custom valid range error: the type `&'static i32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:80:34 + --> $DIR/uninitialized-zeroed.rs:81:34 | LL | let _val: &'static i32 = mem::transmute(0usize); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -364,7 +356,7 @@ LL | let _val: &'static i32 = mem::transmute(0usize); = note: References must be non-null error: the type `&'static [i32]` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:81:36 + --> $DIR/uninitialized-zeroed.rs:82:36 | LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -375,7 +367,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:82:32 + --> $DIR/uninitialized-zeroed.rs:83:32 | LL | let _val: NonZeroU32 = mem::transmute(0); | ^^^^^^^^^^^^^^^^^