Skip to content

Commit cdedae8

Browse files
authored
Rollup merge of #74438 - RalfJung:uninit-lint, r=davidtwco
warn about uninitialized multi-variant enums Fixes #73608
2 parents 3b7e286 + 87b4976 commit cdedae8

File tree

3 files changed

+96
-41
lines changed

3 files changed

+96
-41
lines changed

src/librustc_lint/builtin.rs

+23-5
Original file line numberDiff line numberDiff line change
@@ -1922,6 +1922,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
19221922
None
19231923
}
19241924

1925+
/// Test if this enum has several actually "existing" variants.
1926+
/// Zero-sized uninhabited variants do not always have a tag assigned and thus do not "exist".
1927+
fn is_multi_variant(adt: &ty::AdtDef) -> bool {
1928+
// As an approximation, we only count dataless variants. Those are definitely inhabited.
1929+
let existing_variants = adt.variants.iter().filter(|v| v.fields.is_empty()).count();
1930+
existing_variants > 1
1931+
}
1932+
19251933
/// Return `Some` only if we are sure this type does *not*
19261934
/// allow zero initialization.
19271935
fn ty_find_init_error<'tcx>(
@@ -1950,7 +1958,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
19501958
}
19511959
// Recurse and checks for some compound types.
19521960
Adt(adt_def, substs) if !adt_def.is_union() => {
1953-
// First check f this ADT has a layout attribute (like `NonNull` and friends).
1961+
// First check if this ADT has a layout attribute (like `NonNull` and friends).
19541962
use std::ops::Bound;
19551963
match tcx.layout_scalar_valid_range(adt_def.did) {
19561964
// We exploit here that `layout_scalar_valid_range` will never
@@ -2001,10 +2009,20 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
20012009
)
20022010
})
20032011
}
2004-
// Multi-variant enums are tricky: if all but one variant are
2005-
// uninhabited, we might actually do layout like for a single-variant
2006-
// enum, and then even leaving them uninitialized could be okay.
2007-
_ => None, // Conservative fallback for multi-variant enum.
2012+
// Multi-variant enum.
2013+
_ => {
2014+
if init == InitKind::Uninit && is_multi_variant(adt_def) {
2015+
let span = tcx.def_span(adt_def.did);
2016+
Some((
2017+
"enums have to be initialized to a variant".to_string(),
2018+
Some(span),
2019+
))
2020+
} else {
2021+
// In principle, for zero-initialization we could figure out which variant corresponds
2022+
// to tag 0, and check that... but for now we just accept all zero-initializations.
2023+
None
2024+
}
2025+
}
20082026
}
20092027
}
20102028
Tuple(..) => {

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

+19
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ enum WrapEnum<T> { Wrapped(T) }
2323
#[repr(transparent)]
2424
pub(crate) struct NonBig(u64);
2525

26+
/// A two-variant enum, thus needs a tag and may not remain uninitialized.
27+
enum Fruit {
28+
Apple,
29+
Banana,
30+
}
31+
32+
/// Looks like two variants but really only has one.
33+
enum OneFruit {
34+
Apple(!),
35+
Banana,
36+
}
37+
2638
#[allow(unused)]
2739
fn generic<T: 'static>() {
2840
unsafe {
@@ -80,6 +92,9 @@ fn main() {
8092
let _val: NonBig = mem::zeroed();
8193
let _val: NonBig = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
8294

95+
let _val: Fruit = mem::zeroed();
96+
let _val: Fruit = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
97+
8398
// Transmute-from-0
8499
let _val: &'static i32 = mem::transmute(0usize); //~ ERROR: does not permit zero-initialization
85100
let _val: &'static [i32] = mem::transmute((0usize, 0usize)); //~ ERROR: does not permit zero-initialization
@@ -96,5 +111,9 @@ fn main() {
96111
let _val: MaybeUninit<&'static i32> = mem::zeroed();
97112
let _val: i32 = mem::zeroed();
98113
let _val: bool = MaybeUninit::zeroed().assume_init();
114+
// Some things that happen to work due to rustc implementation details,
115+
// but are not guaranteed to keep working.
116+
let _val: i32 = mem::uninitialized();
117+
let _val: OneFruit = mem::uninitialized();
99118
}
100119
}

0 commit comments

Comments
 (0)