Skip to content

Commit af24588

Browse files
committed
invalid_value lint: detect invalid initialization of arrays
1 parent df20355 commit af24588

File tree

3 files changed

+68
-18
lines changed

3 files changed

+68
-18
lines changed

compiler/rustc_lint/src/builtin.rs

+16-7
Original file line numberDiff line numberDiff line change
@@ -2548,7 +2548,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
25482548
/// Return `Some` only if we are sure this type does *not*
25492549
/// allow zero initialization.
25502550
fn ty_find_init_error<'tcx>(
2551-
tcx: TyCtxt<'tcx>,
2551+
cx: &LateContext<'tcx>,
25522552
ty: Ty<'tcx>,
25532553
init: InitKind,
25542554
) -> Option<InitError> {
@@ -2575,7 +2575,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
25752575
Adt(adt_def, substs) if !adt_def.is_union() => {
25762576
// First check if this ADT has a layout attribute (like `NonNull` and friends).
25772577
use std::ops::Bound;
2578-
match tcx.layout_scalar_valid_range(adt_def.did()) {
2578+
match cx.tcx.layout_scalar_valid_range(adt_def.did()) {
25792579
// We exploit here that `layout_scalar_valid_range` will never
25802580
// return `Bound::Excluded`. (And we have tests checking that we
25812581
// handle the attribute correctly.)
@@ -2603,12 +2603,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
26032603
// Proceed recursively, check all fields.
26042604
let variant = &adt_def.variant(VariantIdx::from_u32(0));
26052605
variant.fields.iter().find_map(|field| {
2606-
ty_find_init_error(tcx, field.ty(tcx, substs), init).map(
2606+
ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(
26072607
|(mut msg, span)| {
26082608
if span.is_none() {
26092609
// Point to this field, should be helpful for figuring
26102610
// out where the source of the error is.
2611-
let span = tcx.def_span(field.did);
2611+
let span = cx.tcx.def_span(field.did);
26122612
write!(
26132613
&mut msg,
26142614
" (in this {} field)",
@@ -2627,7 +2627,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
26272627
// Multi-variant enum.
26282628
_ => {
26292629
if init == InitKind::Uninit && is_multi_variant(*adt_def) {
2630-
let span = tcx.def_span(adt_def.did());
2630+
let span = cx.tcx.def_span(adt_def.did());
26312631
Some((
26322632
"enums have to be initialized to a variant".to_string(),
26332633
Some(span),
@@ -2642,7 +2642,16 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
26422642
}
26432643
Tuple(..) => {
26442644
// Proceed recursively, check all fields.
2645-
ty.tuple_fields().iter().find_map(|field| ty_find_init_error(tcx, field, init))
2645+
ty.tuple_fields().iter().find_map(|field| ty_find_init_error(cx, field, init))
2646+
}
2647+
Array(ty, len) => {
2648+
if matches!(len.try_eval_usize(cx.tcx, cx.param_env), Some(v) if v > 0) {
2649+
// Array length known at array non-empty -- recurse.
2650+
ty_find_init_error(cx, *ty, init)
2651+
} else {
2652+
// Empty array or size unknown.
2653+
None
2654+
}
26462655
}
26472656
// Conservative fallback.
26482657
_ => None,
@@ -2655,7 +2664,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
26552664
// We are extremely conservative with what we warn about.
26562665
let conjured_ty = cx.typeck_results().expr_ty(expr);
26572666
if let Some((msg, span)) =
2658-
with_no_trimmed_paths!(ty_find_init_error(cx.tcx, conjured_ty, init))
2667+
with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
26592668
{
26602669
cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| {
26612670
let mut err = lint.build(&format!(

src/test/ui/lint/uninitialized-zeroed.rs

+8
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ fn main() {
8181
let _val: *const dyn Send = mem::zeroed(); //~ ERROR: does not permit zero-initialization
8282
let _val: *const dyn Send = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
8383

84+
let _val: [fn(); 2] = mem::zeroed(); //~ ERROR: does not permit zero-initialization
85+
let _val: [fn(); 2] = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
86+
8487
// Things that can be zero, but not uninit.
8588
let _val: bool = mem::zeroed();
8689
let _val: bool = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
@@ -94,6 +97,9 @@ fn main() {
9497
let _val: Fruit = mem::zeroed();
9598
let _val: Fruit = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
9699

100+
let _val: [bool; 2] = mem::zeroed();
101+
let _val: [bool; 2] = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
102+
97103
// Transmute-from-0
98104
let _val: &'static i32 = mem::transmute(0usize); //~ ERROR: does not permit zero-initialization
99105
let _val: &'static [i32] = mem::transmute((0usize, 0usize)); //~ ERROR: does not permit zero-initialization
@@ -110,6 +116,8 @@ fn main() {
110116
let _val: MaybeUninit<&'static i32> = mem::zeroed();
111117
let _val: i32 = mem::zeroed();
112118
let _val: bool = MaybeUninit::zeroed().assume_init();
119+
let _val: [bool; 0] = MaybeUninit::uninit().assume_init();
120+
let _val: [!; 0] = MaybeUninit::zeroed().assume_init();
113121
// Some things that happen to work due to rustc implementation details,
114122
// but are not guaranteed to keep working.
115123
let _val: i32 = mem::uninitialized();

src/test/ui/lint/uninitialized-zeroed.stderr

+44-11
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,30 @@ LL | let _val: *const dyn Send = mem::uninitialized();
329329
|
330330
= note: the vtable of a wide raw pointer must be non-null
331331

332+
error: the type `[fn(); 2]` does not permit zero-initialization
333+
--> $DIR/uninitialized-zeroed.rs:84:31
334+
|
335+
LL | let _val: [fn(); 2] = mem::zeroed();
336+
| ^^^^^^^^^^^^^
337+
| |
338+
| this code causes undefined behavior when executed
339+
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
340+
|
341+
= note: function pointers must be non-null
342+
343+
error: the type `[fn(); 2]` does not permit being left uninitialized
344+
--> $DIR/uninitialized-zeroed.rs:85:31
345+
|
346+
LL | let _val: [fn(); 2] = mem::uninitialized();
347+
| ^^^^^^^^^^^^^^^^^^^^
348+
| |
349+
| this code causes undefined behavior when executed
350+
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
351+
|
352+
= note: function pointers must be non-null
353+
332354
error: the type `bool` does not permit being left uninitialized
333-
--> $DIR/uninitialized-zeroed.rs:86:26
355+
--> $DIR/uninitialized-zeroed.rs:89:26
334356
|
335357
LL | let _val: bool = mem::uninitialized();
336358
| ^^^^^^^^^^^^^^^^^^^^
@@ -341,7 +363,7 @@ LL | let _val: bool = mem::uninitialized();
341363
= note: booleans must be either `true` or `false`
342364

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

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

369391
error: the type `Fruit` does not permit being left uninitialized
370-
--> $DIR/uninitialized-zeroed.rs:95:27
392+
--> $DIR/uninitialized-zeroed.rs:98:27
371393
|
372394
LL | let _val: Fruit = mem::uninitialized();
373395
| ^^^^^^^^^^^^^^^^^^^^
@@ -384,8 +406,19 @@ LL | | Banana,
384406
LL | | }
385407
| |_^
386408

409+
error: the type `[bool; 2]` does not permit being left uninitialized
410+
--> $DIR/uninitialized-zeroed.rs:101:31
411+
|
412+
LL | let _val: [bool; 2] = mem::uninitialized();
413+
| ^^^^^^^^^^^^^^^^^^^^
414+
| |
415+
| this code causes undefined behavior when executed
416+
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
417+
|
418+
= note: booleans must be either `true` or `false`
419+
387420
error: the type `&i32` does not permit zero-initialization
388-
--> $DIR/uninitialized-zeroed.rs:98:34
421+
--> $DIR/uninitialized-zeroed.rs:104:34
389422
|
390423
LL | let _val: &'static i32 = mem::transmute(0usize);
391424
| ^^^^^^^^^^^^^^^^^^^^^^
@@ -396,7 +429,7 @@ LL | let _val: &'static i32 = mem::transmute(0usize);
396429
= note: references must be non-null
397430

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

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

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

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

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

453-
error: aborting due to 36 previous errors
486+
error: aborting due to 39 previous errors
454487

0 commit comments

Comments
 (0)