Navigation Menu

Skip to content

Commit

Permalink
warn about uninit bools and chars
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Aug 17, 2019
1 parent 5f7716d commit 25d8a0a
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 7 deletions.
21 changes: 15 additions & 6 deletions src/librustc_lint/builtin.rs
Expand Up @@ -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".
Expand Down Expand Up @@ -1911,14 +1911,23 @@ 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<InitError> {
fn ty_find_init_error<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
init: InitKind,
) -> Option<InitError> {
use rustc::ty::TyKind::*;
match ty.sty {
// Primitive types that don't like 0 as a value.
Ref(..) => Some((format!("References must be non-null"), None)),
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() {
Expand All @@ -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.
Expand All @@ -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,
}
Expand All @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/lint/uninitialized-zeroed.rs
Expand Up @@ -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<char> = 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();
Expand Down
28 changes: 27 additions & 1 deletion src/test/ui/lint/uninitialized-zeroed.stderr
Expand Up @@ -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<T>` instead
|
= note: Booleans must be `true` or `false`

error: the type `Wrap<char>` does not permit being left uninitialized
--> $DIR/uninitialized-zeroed.rs:60:32
|
LL | let _val: Wrap<char> = mem::uninitialized();
| ^^^^^^^^^^^^^^^^^^^^
| |
| this code causes undefined behavior when executed
| help: use `MaybeUninit<T>` instead
|
note: Characters must be a valid unicode codepoint (in this struct field)
--> $DIR/uninitialized-zeroed.rs:16:18
|
LL | struct Wrap<T> { wrapped: T }
| ^^^^^^^^^^

error: aborting due to 24 previous errors

0 comments on commit 25d8a0a

Please sign in to comment.