Skip to content

Commit 02654a0

Browse files
committed
Auto merge of #98919 - 5225225:stricter-invalid-value, r=RalfJung
Strengthen invalid_value lint to forbid uninit primitives, adjust docs to say that's UB For context: #66151 (comment) This does not make it a FCW, but it does explicitly state in the docs that uninit integers are UB. This also doesn't affect any runtime behavior, uninit u32's will still successfully be created through mem::uninitialized.
2 parents 350cca3 + be32460 commit 02654a0

File tree

7 files changed

+81
-19
lines changed

7 files changed

+81
-19
lines changed

compiler/rustc_lint/src/builtin.rs

+9
Original file line numberDiff line numberDiff line change
@@ -2469,6 +2469,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24692469
Char if init == InitKind::Uninit => {
24702470
Some(("characters must be a valid Unicode codepoint".to_string(), None))
24712471
}
2472+
Int(_) | Uint(_) if init == InitKind::Uninit => {
2473+
Some(("integers must not be uninitialized".to_string(), None))
2474+
}
2475+
Float(_) if init == InitKind::Uninit => {
2476+
Some(("floats must not be uninitialized".to_string(), None))
2477+
}
2478+
RawPtr(_) if init == InitKind::Uninit => {
2479+
Some(("raw pointers must not be uninitialized".to_string(), None))
2480+
}
24722481
// Recurse and checks for some compound types.
24732482
Adt(adt_def, substs) if !adt_def.is_union() => {
24742483
// First check if this ADT has a layout attribute (like `NonNull` and friends).

library/core/src/mem/maybe_uninit.rs

-3
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@ use crate::slice;
5454
/// // The equivalent code with `MaybeUninit<i32>`:
5555
/// let x: i32 = unsafe { MaybeUninit::uninit().assume_init() }; // undefined behavior! ⚠️
5656
/// ```
57-
/// (Notice that the rules around uninitialized integers are not finalized yet, but
58-
/// until they are, it is advisable to avoid them.)
59-
///
6057
/// On top of that, remember that most types have additional invariants beyond merely
6158
/// being considered initialized at the type level. For example, a `1`-initialized [`Vec<T>`]
6259
/// is considered initialized (under the current implementation; this does not constitute

library/core/src/mem/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -665,14 +665,14 @@ pub unsafe fn zeroed<T>() -> T {
665665
/// correctly: it has the same effect as [`MaybeUninit::uninit().assume_init()`][uninit].
666666
/// As the [`assume_init` documentation][assume_init] explains,
667667
/// [the Rust compiler assumes][inv] that values are properly initialized.
668-
/// As a consequence, calling e.g. `mem::uninitialized::<bool>()` causes immediate
669-
/// undefined behavior for returning a `bool` that is not definitely either `true`
670-
/// or `false`. Worse, truly uninitialized memory like what gets returned here
668+
///
669+
/// Truly uninitialized memory like what gets returned here
671670
/// is special in that the compiler knows that it does not have a fixed value.
672671
/// This makes it undefined behavior to have uninitialized data in a variable even
673672
/// if that variable has an integer type.
674-
/// (Notice that the rules around uninitialized integers are not finalized yet, but
675-
/// until they are, it is advisable to avoid them.)
673+
///
674+
/// Therefore, it is immediate undefined behavior to call this function on nearly all types,
675+
/// including integer types and arrays of integer types, and even if the result is unused.
676676
///
677677
/// [uninit]: MaybeUninit::uninit
678678
/// [assume_init]: MaybeUninit::assume_init

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

+13-2
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ fn main() {
100100
let _val: [bool; 2] = mem::zeroed();
101101
let _val: [bool; 2] = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
102102

103+
let _val: i32 = mem::zeroed();
104+
let _val: i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
105+
106+
let _val: f32 = mem::zeroed();
107+
let _val: f32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
108+
109+
let _val: *const () = mem::zeroed();
110+
let _val: *const () = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
111+
112+
let _val: *const [()] = mem::zeroed();
113+
let _val: *const [()] = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
114+
103115
// Transmute-from-0
104116
let _val: &'static i32 = mem::transmute(0usize); //~ ERROR: does not permit zero-initialization
105117
let _val: &'static [i32] = mem::transmute((0usize, 0usize)); //~ ERROR: does not permit zero-initialization
@@ -114,13 +126,12 @@ fn main() {
114126
let _val: Option<&'static i32> = mem::zeroed();
115127
let _val: Option<fn()> = mem::zeroed();
116128
let _val: MaybeUninit<&'static i32> = mem::zeroed();
117-
let _val: i32 = mem::zeroed();
118129
let _val: bool = MaybeUninit::zeroed().assume_init();
119130
let _val: [bool; 0] = MaybeUninit::uninit().assume_init();
120131
let _val: [!; 0] = MaybeUninit::zeroed().assume_init();
132+
121133
// Some things that happen to work due to rustc implementation details,
122134
// but are not guaranteed to keep working.
123-
let _val: i32 = mem::uninitialized();
124135
let _val: OneFruit = mem::uninitialized();
125136
}
126137
}

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

+52-8
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ LL | let _val: (i32, !) = mem::uninitialized();
9797
| this code causes undefined behavior when executed
9898
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
9999
|
100-
= note: the `!` type has no valid value
100+
= note: integers must not be uninitialized
101101

102102
error: the type `Void` does not permit zero-initialization
103103
--> $DIR/uninitialized-zeroed.rs:57:26
@@ -414,8 +414,52 @@ LL | let _val: [bool; 2] = mem::uninitialized();
414414
|
415415
= note: booleans must be either `true` or `false`
416416

417+
error: the type `i32` does not permit being left uninitialized
418+
--> $DIR/uninitialized-zeroed.rs:104:25
419+
|
420+
LL | let _val: i32 = mem::uninitialized();
421+
| ^^^^^^^^^^^^^^^^^^^^
422+
| |
423+
| this code causes undefined behavior when executed
424+
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
425+
|
426+
= note: integers must not be uninitialized
427+
428+
error: the type `f32` does not permit being left uninitialized
429+
--> $DIR/uninitialized-zeroed.rs:107:25
430+
|
431+
LL | let _val: f32 = mem::uninitialized();
432+
| ^^^^^^^^^^^^^^^^^^^^
433+
| |
434+
| this code causes undefined behavior when executed
435+
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
436+
|
437+
= note: floats must not be uninitialized
438+
439+
error: the type `*const ()` does not permit being left uninitialized
440+
--> $DIR/uninitialized-zeroed.rs:110:31
441+
|
442+
LL | let _val: *const () = mem::uninitialized();
443+
| ^^^^^^^^^^^^^^^^^^^^
444+
| |
445+
| this code causes undefined behavior when executed
446+
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
447+
|
448+
= note: raw pointers must not be uninitialized
449+
450+
error: the type `*const [()]` does not permit being left uninitialized
451+
--> $DIR/uninitialized-zeroed.rs:113:33
452+
|
453+
LL | let _val: *const [()] = mem::uninitialized();
454+
| ^^^^^^^^^^^^^^^^^^^^
455+
| |
456+
| this code causes undefined behavior when executed
457+
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
458+
|
459+
= note: raw pointers must not be uninitialized
460+
417461
error: the type `&i32` does not permit zero-initialization
418-
--> $DIR/uninitialized-zeroed.rs:104:34
462+
--> $DIR/uninitialized-zeroed.rs:116:34
419463
|
420464
LL | let _val: &'static i32 = mem::transmute(0usize);
421465
| ^^^^^^^^^^^^^^^^^^^^^^
@@ -426,7 +470,7 @@ LL | let _val: &'static i32 = mem::transmute(0usize);
426470
= note: references must be non-null
427471

428472
error: the type `&[i32]` does not permit zero-initialization
429-
--> $DIR/uninitialized-zeroed.rs:105:36
473+
--> $DIR/uninitialized-zeroed.rs:117:36
430474
|
431475
LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize));
432476
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -437,7 +481,7 @@ LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize));
437481
= note: references must be non-null
438482

439483
error: the type `NonZeroU32` does not permit zero-initialization
440-
--> $DIR/uninitialized-zeroed.rs:106:32
484+
--> $DIR/uninitialized-zeroed.rs:118:32
441485
|
442486
LL | let _val: NonZeroU32 = mem::transmute(0);
443487
| ^^^^^^^^^^^^^^^^^
@@ -448,7 +492,7 @@ LL | let _val: NonZeroU32 = mem::transmute(0);
448492
= note: `std::num::NonZeroU32` must be non-null
449493

450494
error: the type `NonNull<i32>` does not permit zero-initialization
451-
--> $DIR/uninitialized-zeroed.rs:109:34
495+
--> $DIR/uninitialized-zeroed.rs:121:34
452496
|
453497
LL | let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init();
454498
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -459,7 +503,7 @@ LL | let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init();
459503
= note: `std::ptr::NonNull<i32>` must be non-null
460504

461505
error: the type `NonNull<i32>` does not permit being left uninitialized
462-
--> $DIR/uninitialized-zeroed.rs:110:34
506+
--> $DIR/uninitialized-zeroed.rs:122:34
463507
|
464508
LL | let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
465509
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -470,7 +514,7 @@ LL | let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
470514
= note: `std::ptr::NonNull<i32>` must be non-null
471515

472516
error: the type `bool` does not permit being left uninitialized
473-
--> $DIR/uninitialized-zeroed.rs:111:26
517+
--> $DIR/uninitialized-zeroed.rs:123:26
474518
|
475519
LL | let _val: bool = MaybeUninit::uninit().assume_init();
476520
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -480,5 +524,5 @@ LL | let _val: bool = MaybeUninit::uninit().assume_init();
480524
|
481525
= note: booleans must be either `true` or `false`
482526

483-
error: aborting due to 39 previous errors
527+
error: aborting due to 43 previous errors
484528

src/test/ui/sanitize/memory.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#![feature(core_intrinsics)]
1515
#![feature(start)]
1616
#![feature(bench_black_box)]
17+
#![allow(invalid_value)]
1718

1819
use std::hint::black_box;
1920
use std::mem::MaybeUninit;

src/tools/clippy/tests/ui/uninit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![feature(stmt_expr_attributes)]
2-
#![allow(clippy::let_unit_value)]
2+
#![allow(clippy::let_unit_value, invalid_value)]
33

44
use std::mem::{self, MaybeUninit};
55

0 commit comments

Comments
 (0)