Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

warn about uninitialized multi-variant enums (invalid_value lint) #74438

Merged
merged 1 commit into from
Jul 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions src/librustc_lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1922,6 +1922,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
None
}

/// Test if this enum has several actually "existing" variants.
/// Zero-sized uninhabited variants do not always have a tag assigned and thus do not "exist".
fn is_multi_variant(adt: &ty::AdtDef) -> bool {
// As an approximation, we only count dataless variants. Those are definitely inhabited.
let existing_variants = adt.variants.iter().filter(|v| v.fields.is_empty()).count();
existing_variants > 1
}

/// Return `Some` only if we are sure this type does *not*
/// allow zero initialization.
fn ty_find_init_error<'tcx>(
Expand Down Expand Up @@ -1950,7 +1958,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
}
// Recurse and checks for some compound types.
Adt(adt_def, substs) if !adt_def.is_union() => {
// First check f this ADT has a layout attribute (like `NonNull` and friends).
// 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) {
// We exploit here that `layout_scalar_valid_range` will never
Expand Down Expand Up @@ -2001,10 +2009,20 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
)
})
}
// Multi-variant enums are tricky: if all but one variant are
// uninhabited, we might actually do layout like for a single-variant
// enum, and then even leaving them uninitialized could be okay.
_ => None, // Conservative fallback for multi-variant enum.
// Multi-variant enum.
_ => {
if init == InitKind::Uninit && is_multi_variant(adt_def) {
let span = tcx.def_span(adt_def.did);
Some((
"enums have to be initialized to a variant".to_string(),
Some(span),
))
} else {
// In principle, for zero-initialization we could figure out which variant corresponds
// to tag 0, and check that... but for now we just accept all zero-initializations.
None
}
}
}
}
Tuple(..) => {
Expand Down
19 changes: 19 additions & 0 deletions src/test/ui/lint/uninitialized-zeroed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ enum WrapEnum<T> { Wrapped(T) }
#[repr(transparent)]
pub(crate) struct NonBig(u64);

/// A two-variant enum, thus needs a tag and may not remain uninitialized.
enum Fruit {
Apple,
Banana,
}

/// Looks like two variants but really only has one.
enum OneFruit {
Apple(!),
Banana,
}

#[allow(unused)]
fn generic<T: 'static>() {
unsafe {
Expand Down Expand Up @@ -80,6 +92,9 @@ fn main() {
let _val: NonBig = mem::zeroed();
let _val: NonBig = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized

let _val: Fruit = mem::zeroed();
let _val: Fruit = 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 @@ -96,5 +111,9 @@ fn main() {
let _val: MaybeUninit<&'static i32> = mem::zeroed();
let _val: i32 = mem::zeroed();
let _val: bool = 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();
let _val: OneFruit = mem::uninitialized();
}
}
Loading