Skip to content

Commit

Permalink
invalid_value lint: detect invalid initialization of arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Apr 1, 2022
1 parent df20355 commit af24588
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 18 deletions.
23 changes: 16 additions & 7 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2548,7 +2548,7 @@ impl<'tcx> LateLintPass<'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>,
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
init: InitKind,
) -> Option<InitError> {
Expand All @@ -2575,7 +2575,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
Adt(adt_def, substs) if !adt_def.is_union() => {
// First check if this ADT has a layout attribute (like `NonNull` and friends).
use std::ops::Bound;
match tcx.layout_scalar_valid_range(adt_def.did()) {
match cx.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.)
Expand Down Expand Up @@ -2603,12 +2603,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
// Proceed recursively, check all fields.
let variant = &adt_def.variant(VariantIdx::from_u32(0));
variant.fields.iter().find_map(|field| {
ty_find_init_error(tcx, field.ty(tcx, substs), init).map(
ty_find_init_error(cx, field.ty(cx.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.
let span = tcx.def_span(field.did);
let span = cx.tcx.def_span(field.did);
write!(
&mut msg,
" (in this {} field)",
Expand All @@ -2627,7 +2627,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
// Multi-variant enum.
_ => {
if init == InitKind::Uninit && is_multi_variant(*adt_def) {
let span = tcx.def_span(adt_def.did());
let span = cx.tcx.def_span(adt_def.did());
Some((
"enums have to be initialized to a variant".to_string(),
Some(span),
Expand All @@ -2642,7 +2642,16 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
}
Tuple(..) => {
// Proceed recursively, check all fields.
ty.tuple_fields().iter().find_map(|field| ty_find_init_error(tcx, field, init))
ty.tuple_fields().iter().find_map(|field| ty_find_init_error(cx, field, init))
}
Array(ty, len) => {
if matches!(len.try_eval_usize(cx.tcx, cx.param_env), Some(v) if v > 0) {
// Array length known at array non-empty -- recurse.
ty_find_init_error(cx, *ty, init)
} else {
// Empty array or size unknown.
None
}
}
// Conservative fallback.
_ => None,
Expand All @@ -2655,7 +2664,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
// We are extremely conservative with what we warn about.
let conjured_ty = cx.typeck_results().expr_ty(expr);
if let Some((msg, span)) =
with_no_trimmed_paths!(ty_find_init_error(cx.tcx, conjured_ty, init))
with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
{
cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| {
let mut err = lint.build(&format!(
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/lint/uninitialized-zeroed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ fn main() {
let _val: *const dyn Send = mem::zeroed(); //~ ERROR: does not permit zero-initialization
let _val: *const dyn Send = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized

let _val: [fn(); 2] = mem::zeroed(); //~ ERROR: does not permit zero-initialization
let _val: [fn(); 2] = 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
Expand All @@ -94,6 +97,9 @@ fn main() {
let _val: Fruit = mem::zeroed();
let _val: Fruit = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized

let _val: [bool; 2] = mem::zeroed();
let _val: [bool; 2] = 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
Expand All @@ -110,6 +116,8 @@ fn main() {
let _val: MaybeUninit<&'static i32> = mem::zeroed();
let _val: i32 = mem::zeroed();
let _val: bool = MaybeUninit::zeroed().assume_init();
let _val: [bool; 0] = MaybeUninit::uninit().assume_init();
let _val: [!; 0] = MaybeUninit::zeroed().assume_init();
// Some things that happen to work due to rustc implementation details,
// but are not guaranteed to keep working.
let _val: i32 = mem::uninitialized();
Expand Down
55 changes: 44 additions & 11 deletions src/test/ui/lint/uninitialized-zeroed.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,30 @@ LL | let _val: *const dyn Send = mem::uninitialized();
|
= note: the vtable of a wide raw pointer must be non-null

error: the type `[fn(); 2]` does not permit zero-initialization
--> $DIR/uninitialized-zeroed.rs:84:31
|
LL | let _val: [fn(); 2] = mem::zeroed();
| ^^^^^^^^^^^^^
| |
| this code causes undefined behavior when executed
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
= note: function pointers must be non-null

error: the type `[fn(); 2]` does not permit being left uninitialized
--> $DIR/uninitialized-zeroed.rs:85:31
|
LL | let _val: [fn(); 2] = mem::uninitialized();
| ^^^^^^^^^^^^^^^^^^^^
| |
| this code causes undefined behavior when executed
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
= note: function pointers must be non-null

error: the type `bool` does not permit being left uninitialized
--> $DIR/uninitialized-zeroed.rs:86:26
--> $DIR/uninitialized-zeroed.rs:89:26
|
LL | let _val: bool = mem::uninitialized();
| ^^^^^^^^^^^^^^^^^^^^
Expand All @@ -341,7 +363,7 @@ LL | let _val: bool = mem::uninitialized();
= note: booleans must be either `true` or `false`

error: the type `Wrap<char>` does not permit being left uninitialized
--> $DIR/uninitialized-zeroed.rs:89:32
--> $DIR/uninitialized-zeroed.rs:92:32
|
LL | let _val: Wrap<char> = mem::uninitialized();
| ^^^^^^^^^^^^^^^^^^^^
Expand All @@ -356,7 +378,7 @@ LL | struct Wrap<T> { wrapped: T }
| ^^^^^^^^^^

error: the type `NonBig` does not permit being left uninitialized
--> $DIR/uninitialized-zeroed.rs:92:28
--> $DIR/uninitialized-zeroed.rs:95:28
|
LL | let _val: NonBig = mem::uninitialized();
| ^^^^^^^^^^^^^^^^^^^^
Expand All @@ -367,7 +389,7 @@ LL | let _val: NonBig = mem::uninitialized();
= note: `NonBig` must be initialized inside its custom valid range

error: the type `Fruit` does not permit being left uninitialized
--> $DIR/uninitialized-zeroed.rs:95:27
--> $DIR/uninitialized-zeroed.rs:98:27
|
LL | let _val: Fruit = mem::uninitialized();
| ^^^^^^^^^^^^^^^^^^^^
Expand All @@ -384,8 +406,19 @@ LL | | Banana,
LL | | }
| |_^

error: the type `[bool; 2]` does not permit being left uninitialized
--> $DIR/uninitialized-zeroed.rs:101:31
|
LL | let _val: [bool; 2] = mem::uninitialized();
| ^^^^^^^^^^^^^^^^^^^^
| |
| this code causes undefined behavior when executed
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
= note: booleans must be either `true` or `false`

error: the type `&i32` does not permit zero-initialization
--> $DIR/uninitialized-zeroed.rs:98:34
--> $DIR/uninitialized-zeroed.rs:104:34
|
LL | let _val: &'static i32 = mem::transmute(0usize);
| ^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -396,7 +429,7 @@ LL | let _val: &'static i32 = mem::transmute(0usize);
= note: references must be non-null

error: the type `&[i32]` does not permit zero-initialization
--> $DIR/uninitialized-zeroed.rs:99:36
--> $DIR/uninitialized-zeroed.rs:105:36
|
LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -407,7 +440,7 @@ LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize));
= note: references must be non-null

error: the type `NonZeroU32` does not permit zero-initialization
--> $DIR/uninitialized-zeroed.rs:100:32
--> $DIR/uninitialized-zeroed.rs:106:32
|
LL | let _val: NonZeroU32 = mem::transmute(0);
| ^^^^^^^^^^^^^^^^^
Expand All @@ -418,7 +451,7 @@ LL | let _val: NonZeroU32 = mem::transmute(0);
= note: `std::num::NonZeroU32` must be non-null

error: the type `NonNull<i32>` does not permit zero-initialization
--> $DIR/uninitialized-zeroed.rs:103:34
--> $DIR/uninitialized-zeroed.rs:109:34
|
LL | let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -429,7 +462,7 @@ LL | let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init();
= note: `std::ptr::NonNull<i32>` must be non-null

error: the type `NonNull<i32>` does not permit being left uninitialized
--> $DIR/uninitialized-zeroed.rs:104:34
--> $DIR/uninitialized-zeroed.rs:110:34
|
LL | let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -440,7 +473,7 @@ LL | let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
= note: `std::ptr::NonNull<i32>` must be non-null

error: the type `bool` does not permit being left uninitialized
--> $DIR/uninitialized-zeroed.rs:105:26
--> $DIR/uninitialized-zeroed.rs:111:26
|
LL | let _val: bool = MaybeUninit::uninit().assume_init();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -450,5 +483,5 @@ LL | let _val: bool = MaybeUninit::uninit().assume_init();
|
= note: booleans must be either `true` or `false`

error: aborting due to 36 previous errors
error: aborting due to 39 previous errors

0 comments on commit af24588

Please sign in to comment.