Skip to content

Commit 25d8a0a

Browse files
committed
warn about uninit bools and chars
1 parent 5f7716d commit 25d8a0a

File tree

3 files changed

+45
-7
lines changed

3 files changed

+45
-7
lines changed

src/librustc_lint/builtin.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,7 +1876,7 @@ declare_lint_pass!(InvalidValue => [INVALID_VALUE]);
18761876
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
18771877
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &hir::Expr) {
18781878

1879-
#[derive(Debug)]
1879+
#[derive(Debug, Copy, Clone, PartialEq)]
18801880
enum InitKind { Zeroed, Uninit };
18811881

18821882
/// Determine if this expression is a "dangerous initialization".
@@ -1911,14 +1911,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
19111911

19121912
/// Return `Some` only if we are sure this type does *not*
19131913
/// allow zero initialization.
1914-
fn ty_find_init_error<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<InitError> {
1914+
fn ty_find_init_error<'tcx>(
1915+
tcx: TyCtxt<'tcx>,
1916+
ty: Ty<'tcx>,
1917+
init: InitKind,
1918+
) -> Option<InitError> {
19151919
use rustc::ty::TyKind::*;
19161920
match ty.sty {
19171921
// Primitive types that don't like 0 as a value.
19181922
Ref(..) => Some((format!("References must be non-null"), None)),
19191923
Adt(..) if ty.is_box() => Some((format!("`Box` must be non-null"), None)),
19201924
FnPtr(..) => Some((format!("Function pointers must be non-null"), None)),
19211925
Never => Some((format!("The never type (`!`) has no valid value"), None)),
1926+
// Primitive types with other constraints
1927+
Bool if init == InitKind::Uninit =>
1928+
Some((format!("Booleans must be `true` or `false`"), None)),
1929+
Char if init == InitKind::Uninit =>
1930+
Some((format!("Characters must be a valid unicode codepoint"), None)),
19221931
// Recurse for some compound types.
19231932
Adt(adt_def, substs) if !adt_def.is_union() => {
19241933
match adt_def.variants.len() {
@@ -1931,6 +1940,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
19311940
ty_find_init_error(
19321941
tcx,
19331942
field.ty(tcx, substs),
1943+
init,
19341944
).map(|(mut msg, span)| if span.is_none() {
19351945
// Point to this field, should be helpful for figuring
19361946
// out where the source of the error is.
@@ -1949,11 +1959,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
19491959
}
19501960
Tuple(..) => {
19511961
// Proceed recursively, check all fields.
1952-
ty.tuple_fields().find_map(|field| ty_find_init_error(tcx, field))
1962+
ty.tuple_fields().find_map(|field| ty_find_init_error(tcx, field, init))
19531963
}
19541964
// FIXME: Would be nice to also warn for `NonNull`/`NonZero*`.
1955-
// FIXME: *Only for `mem::uninitialized`*, we could also warn for `bool`,
1956-
// `char`, and any multivariant enum.
1965+
// FIXME: *Only for `mem::uninitialized`*, we could also warn for multivariant enum.
19571966
// Conservative fallback.
19581967
_ => None,
19591968
}
@@ -1964,7 +1973,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
19641973
// using zeroed or uninitialized memory.
19651974
// We are extremely conservative with what we warn about.
19661975
let conjured_ty = cx.tables.expr_ty(expr);
1967-
if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty) {
1976+
if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty, init) {
19681977
let mut err = cx.struct_span_lint(
19691978
INVALID_VALUE,
19701979
expr.span,

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ fn main() {
5656
let _val: Wrap<(RefPair, i32)> = mem::zeroed(); //~ ERROR: does not permit zero-initialization
5757
let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
5858

59+
let _val: bool = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
60+
let _val: Wrap<char> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
61+
5962
// Some types that should work just fine.
6063
let _val: Option<&'static i32> = mem::zeroed();
6164
let _val: Option<fn()> = mem::zeroed();

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,5 +285,31 @@ note: References must be non-null (in this struct field)
285285
LL | struct RefPair((&'static i32, i32));
286286
| ^^^^^^^^^^^^^^^^^^^
287287

288-
error: aborting due to 22 previous errors
288+
error: the type `bool` does not permit being left uninitialized
289+
--> $DIR/uninitialized-zeroed.rs:59:26
290+
|
291+
LL | let _val: bool = mem::uninitialized();
292+
| ^^^^^^^^^^^^^^^^^^^^
293+
| |
294+
| this code causes undefined behavior when executed
295+
| help: use `MaybeUninit<T>` instead
296+
|
297+
= note: Booleans must be `true` or `false`
298+
299+
error: the type `Wrap<char>` does not permit being left uninitialized
300+
--> $DIR/uninitialized-zeroed.rs:60:32
301+
|
302+
LL | let _val: Wrap<char> = mem::uninitialized();
303+
| ^^^^^^^^^^^^^^^^^^^^
304+
| |
305+
| this code causes undefined behavior when executed
306+
| help: use `MaybeUninit<T>` instead
307+
|
308+
note: Characters must be a valid unicode codepoint (in this struct field)
309+
--> $DIR/uninitialized-zeroed.rs:16:18
310+
|
311+
LL | struct Wrap<T> { wrapped: T }
312+
| ^^^^^^^^^^
313+
314+
error: aborting due to 24 previous errors
289315

0 commit comments

Comments
 (0)