From 641251b16d19a793a68d234cf888a16f5bc61064 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Thu, 11 Aug 2022 22:04:28 +0200 Subject: [PATCH] Add new `CheckMaybeUninit` MIR transform This MIR transform inserts the same validity checks from `mem::{uninitialized,zeroed}` to `MaybeUninit::{uninit,zeroed}().assume_init()`. We have been panicking in `mem::uninit` on invalid values for quite some time now, and it has helped to get people off the unsound API and towards using `MaybeUninit`. While correct usage of `MaybeUninit` is clearly documented, some people still use it incorrectly and simply replaced their wrong `mem::uninit` usage with `MaybeUninit::uninit().assume_init()`. This is not any more correct than the old version, and we should still emit panics in these cases. As this can't be done in the library only, we need this MIR pass to insert the calls. For now, it only detects direct usages of `MaybeUninit::uninit().assume_init()` but it could be extended in the future to do more advanced dataflow analysis. --- compiler/rustc_hir/src/lang_items.rs | 4 + compiler/rustc_middle/src/mir/mod.rs | 5 + .../src/check_maybe_uninit.rs | 164 ++++++ compiler/rustc_mir_transform/src/lib.rs | 4 + compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics.rs | 46 ++ library/core/src/lib.rs | 1 + library/core/src/mem/maybe_uninit.rs | 4 + ...ck_maybe_uninit.main.CheckMaybeUninit.diff | 90 +++ tests/mir-opt/check_maybe_uninit.rs | 9 + tests/ui/consts/assert-type-intrinsics.stderr | 72 ++- .../maybe_uninit_checks_consts_invalid.rs | 15 + .../maybe_uninit_checks_consts_invalid.stderr | 72 +++ .../maybe_uninit_checks_consts_valid.rs | 13 + .../intrinsics/panic-uninitialized-zeroed.rs | 554 +++++++----------- 15 files changed, 712 insertions(+), 342 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/check_maybe_uninit.rs create mode 100644 tests/mir-opt/check_maybe_uninit.main.CheckMaybeUninit.diff create mode 100644 tests/mir-opt/check_maybe_uninit.rs create mode 100644 tests/ui/intrinsics/maybe_uninit_checks_consts_invalid.rs create mode 100644 tests/ui/intrinsics/maybe_uninit_checks_consts_invalid.stderr create mode 100644 tests/ui/intrinsics/maybe_uninit_checks_consts_valid.rs diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 0454633091568..bd00202bf8717 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -328,6 +328,10 @@ language_item_table! { RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; String, sym::String, string, Target::Struct, GenericRequirement::None; + + // Lang item because the compiler inserts calls to it when uninit memory is used + AssertUninitValid, sym::assert_uninit_valid, assert_uninit_valid, Target::Fn, GenericRequirement::Exact(1); + AssertZeroValid, sym::assert_zero_valid, assert_zero_valid, Target::Fn, GenericRequirement::Exact(1); } pub enum GenericRequirement { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 46184cddd51f5..16b101e06852a 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -3022,6 +3022,11 @@ impl fmt::Debug for Location { impl Location { pub const START: Location = Location { block: START_BLOCK, statement_index: 0 }; + /// Create a new location at the start of a basic block. + pub fn start_of_block(block: BasicBlock) -> Self { + Self { block, statement_index: 0 } + } + /// Returns the location immediately after this one within the enclosing block. /// /// Note that if this location represents a terminator, then the diff --git a/compiler/rustc_mir_transform/src/check_maybe_uninit.rs b/compiler/rustc_mir_transform/src/check_maybe_uninit.rs new file mode 100644 index 0000000000000..f89b4b900ce34 --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_maybe_uninit.rs @@ -0,0 +1,164 @@ +//! This pass inserts the same validity checks into `MaybeUninit::{uninit,zeroed}().assert_init()` +//! as in `mem::{uninitialized,zeroed}`. +//! +//! Note that this module uses `uninit` to mean `uninit` or `zeroed` unless `zeroed` is used explicitly. +//! +//! It does this by first finding a call to `MaybeUninit::uninit`, and then figuring out +//! whether the successor basic block is a call to `MaybeUninit::assume_init` on the same local. + +use rustc_const_eval::interpret; +use rustc_hir::def_id::DefId; +use rustc_middle::mir::patch::MirPatch; +use rustc_middle::mir::{ + BasicBlock, BasicBlockData, Body, Constant, ConstantKind, Operand, Place, SourceInfo, + Terminator, TerminatorKind, +}; +use rustc_middle::ty::{self, List, SubstsRef, TyCtxt}; +use rustc_span::{sym, Span}; + +use crate::MirPass; + +pub struct CheckMaybeUninit; + +impl<'tcx> MirPass<'tcx> for CheckMaybeUninit { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let mut patch = MirPatch::new(body); + + for (mu_uninit_bb, _) in body.basic_blocks.iter_enumerated() { + let terminator = body.basic_blocks[mu_uninit_bb].terminator(); + + let TerminatorKind::Call { + func: mu_uninit_func, + target: assume_init_bb, + destination: uninit_place, + .. + } = &terminator.kind else { + continue; + }; + + let Some((mu_method_def_id, substs)) = mu_uninit_func.const_fn_def() else { + continue; + }; + + let Some(assume_init_bb) = assume_init_bb else { + continue; + }; + + let Some((assume_init_operand, assume_init_call_span)) = is_block_just_assume_init(tcx, &body.basic_blocks[*assume_init_bb]) else { + continue; + }; + + let Some(assume_init_place) = assume_init_operand.place() else { + continue; + }; + + if assume_init_place != *uninit_place { + // The calls here are a little sketchy, but the place that is assumed to be init is not the place that was just crated + // as uninit, so we conservatively bail out. + continue; + } + + // Select the right assertion intrinsic to call depending on which MaybeUninit method we called + let Some(init_check_def_id) = get_init_check_def_id(tcx, mu_method_def_id) else { + continue; + }; + + let assert_valid_bb = make_assert_valid_bb( + &mut patch, + tcx, + assume_init_call_span, + init_check_def_id, + *assume_init_bb, + substs, + ); + + let mut new_uninit_terminator = terminator.kind.clone(); + match new_uninit_terminator { + TerminatorKind::Call { ref mut target, .. } => { + *target = Some(assert_valid_bb); + } + _ => unreachable!("terminator must be TerminatorKind::Call as checked above"), + } + + patch.patch_terminator(mu_uninit_bb, new_uninit_terminator); + } + + patch.apply(body); + } +} + +fn is_block_just_assume_init<'tcx, 'blk>( + tcx: TyCtxt<'tcx>, + block: &'blk BasicBlockData<'tcx>, +) -> Option<(&'blk Operand<'tcx>, Span)> { + if block.statements.is_empty() + && let TerminatorKind::Call { + func, + args, + fn_span, + .. + } = &block.terminator().kind + && let Some((def_id, _)) = func.const_fn_def() + && tcx.is_diagnostic_item(sym::assume_init, def_id) + { + args.get(0).map(|operand| (operand, *fn_span)) + } else { + None + } +} + +fn get_init_check_def_id(tcx: TyCtxt<'_>, mu_method_def_id: DefId) -> Option { + if tcx.is_diagnostic_item(sym::maybe_uninit_uninit, mu_method_def_id) { + tcx.lang_items().assert_uninit_valid() + } else if tcx.is_diagnostic_item(sym::maybe_uninit_zeroed, mu_method_def_id) { + tcx.lang_items().assert_zero_valid() + } else { + None + } +} + +fn make_assert_valid_bb<'tcx>( + patch: &mut MirPatch<'tcx>, + tcx: TyCtxt<'tcx>, + fn_span: Span, + init_check_def_id: DefId, + target_bb: BasicBlock, + substs: SubstsRef<'tcx>, +) -> BasicBlock { + let func = make_fn_operand_for_assert_valid(tcx, init_check_def_id, fn_span, substs); + + let local = patch.new_temp(tcx.types.unit, fn_span); + + let terminator = TerminatorKind::Call { + func, + args: vec![], + destination: Place { local, projection: List::empty() }, + target: Some(target_bb), + cleanup: Some(patch.resume_block()), + from_hir_call: true, + fn_span, + }; + + let terminator = Terminator { source_info: SourceInfo::outermost(fn_span), kind: terminator }; + + let bb_data = BasicBlockData::new(Some(terminator)); + + let block = patch.new_block(bb_data); + block +} + +fn make_fn_operand_for_assert_valid<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + span: Span, + substs: SubstsRef<'tcx>, +) -> Operand<'tcx> { + let fn_ty = ty::FnDef(def_id, substs); + let fn_ty = tcx.mk_ty(fn_ty); + + Operand::Constant(Box::new(Constant { + span, + literal: ConstantKind::Val(interpret::ConstValue::ZeroSized, fn_ty), + user_ty: None, + })) +} diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 45cd4024c9f57..e2ae40d251cbd 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -48,6 +48,7 @@ mod add_call_guards; mod add_moves_for_packed_drops; mod add_retag; mod check_const_item_mutation; +mod check_maybe_uninit; mod check_packed_ref; pub mod check_unsafety; // This pass is public to allow external drivers to perform MIR cleanup @@ -300,6 +301,7 @@ fn mir_const(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> &Steal< &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), // What we need to do constant evaluation. + &check_maybe_uninit::CheckMaybeUninit, &simplify::SimplifyCfg::new("initial"), &rustc_peek::SanityCheck, // Just a lint ], @@ -497,6 +499,8 @@ fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx> /// After this series of passes, no lifetime analysis based on borrowing can be done. fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let passes: &[&dyn MirPass<'tcx>] = &[ + // FIXME: Preferably we'd run this before const eval once the stability of the wrapper function is figured out + &check_maybe_uninit::CheckMaybeUninit, &cleanup_post_borrowck::CleanupPostBorrowck, &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::new("early-opt"), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 37d2aea42ad8f..97432644551e5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -383,6 +383,7 @@ symbols! { assert_mem_uninitialized_valid, assert_ne_macro, assert_receiver_is_total_eq, + assert_uninit_valid, assert_zero_valid, asserting, associated_const_equality, diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index b1ed3b31e430c..945491929a5f9 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -964,6 +964,14 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] pub fn assert_mem_uninitialized_valid(); + /// A guard for `std::mem::uninitialized`. This will statically either panic, or do nothing. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_const_unstable(feature = "const_assert_type2", issue = "none")] + #[rustc_safe_intrinsic] + #[cfg(bootstrap)] + pub fn assert_uninit_valid(); + /// Gets a reference to a static `Location` indicating where it was called. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2527,3 +2535,41 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { write_bytes(dst, val, count) } } + +// Wrappers around the init assertion intrinsics. Calls to these +// are inserted in the check_maybe_uninit mir pass + +#[unstable( + feature = "maybe_uninit_checks_internals", + issue = "none", + reason = "implementation detail of panics on invalid MaybeUninit usage" +)] +#[rustc_allow_const_fn_unstable(const_assert_type2)] +#[rustc_const_stable( + feature = "const_maybe_uninit_checks_internals", + since = "CURRENT_RUSTC_VERSION" +)] +#[cfg_attr(not(bootstrap), lang = "assert_zero_valid")] +#[track_caller] +pub const fn assert_zero_valid_wrapper() { + assert_zero_valid::(); +} + +#[unstable( + feature = "maybe_uninit_checks_internals", + issue = "none", + reason = "implementation detail of panics on invalid MaybeUninit usage" +)] +#[rustc_allow_const_fn_unstable(const_assert_type2)] +#[rustc_const_stable( + feature = "const_maybe_uninit_checks_internals", + since = "CURRENT_RUSTC_VERSION" +)] +#[cfg_attr(not(bootstrap), lang = "assert_uninit_valid")] +#[track_caller] +pub const fn assert_uninit_valid_wrapper() { + #[cfg(bootstrap)] + assert_uninit_valid::(); + #[cfg(not(bootstrap))] + assert_mem_uninitialized_valid::(); +} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index dc0702c467a4e..519348d6c7160 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -103,6 +103,7 @@ #![feature(const_align_of_val_raw)] #![feature(const_alloc_layout)] #![feature(const_arguments_as_str)] +#![feature(const_assert_type2)] #![feature(const_array_into_iter_constructors)] #![feature(const_bigint_helper_methods)] #![feature(const_black_box)] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 3f491836551dc..23f948850e46b 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -624,6 +624,10 @@ impl MaybeUninit { // This also means that `self` must be a `value` variant. unsafe { intrinsics::assert_inhabited::(); + + // When this function is called directly after >::uninit, we insert calls to + // `intrinsics::assert_zero_valid_wrapper` or `intrinsics::assert_uninit_valid_wrapper` respectively. + ManuallyDrop::into_inner(self.value) } } diff --git a/tests/mir-opt/check_maybe_uninit.main.CheckMaybeUninit.diff b/tests/mir-opt/check_maybe_uninit.main.CheckMaybeUninit.diff new file mode 100644 index 0000000000000..606ac88ff246c --- /dev/null +++ b/tests/mir-opt/check_maybe_uninit.main.CheckMaybeUninit.diff @@ -0,0 +1,90 @@ +- // MIR for `main` before CheckMaybeUninit ++ // MIR for `main` after CheckMaybeUninit + + | User Type Annotations + | 0: user_ty: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: TypeOf(DefId(2:2022 ~ core[4f75]::mem::maybe_uninit::{impl#2}::uninit), UserSubsts { substs: [^0], user_self_ty: Some(UserSelfTy { impl_def_id: DefId(2:2019 ~ core[4f75]::mem::maybe_uninit::{impl#2}), self_ty: std::mem::MaybeUninit }) }) }, span: $DIR/check-maybe-uninit.rs:6:17: 6:42, inferred_ty: fn() -> std::mem::MaybeUninit {std::mem::MaybeUninit::::uninit} + | 1: user_ty: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: TypeOf(DefId(2:2022 ~ core[4f75]::mem::maybe_uninit::{impl#2}::uninit), UserSubsts { substs: [^0], user_self_ty: Some(UserSelfTy { impl_def_id: DefId(2:2019 ~ core[4f75]::mem::maybe_uninit::{impl#2}), self_ty: std::mem::MaybeUninit }) }) }, span: $DIR/check-maybe-uninit.rs:7:17: 7:46, inferred_ty: fn() -> std::mem::MaybeUninit {std::mem::MaybeUninit::::uninit} + | + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/check-maybe-uninit.rs:+0:11: +0:11 + let mut _1: u8; // in scope 0 at $DIR/check-maybe-uninit.rs:+2:17: +2:58 + let mut _2: std::mem::MaybeUninit; // in scope 0 at $DIR/check-maybe-uninit.rs:+2:17: +2:44 + let mut _3: std::string::String; // in scope 0 at $DIR/check-maybe-uninit.rs:+3:17: +3:62 + let mut _4: std::mem::MaybeUninit; // in scope 0 at $DIR/check-maybe-uninit.rs:+3:17: +3:48 ++ let mut _5: (); // in scope 0 at $DIR/check-maybe-uninit.rs:+2:45: +2:58 ++ let mut _6: (); // in scope 0 at $DIR/check-maybe-uninit.rs:+3:49: +3:62 + scope 1 { + scope 2 { + scope 3 { + } + } + } + + bb0: { + StorageLive(_1); // scope 1 at $DIR/check-maybe-uninit.rs:+2:17: +2:58 + StorageLive(_2); // scope 1 at $DIR/check-maybe-uninit.rs:+2:17: +2:44 +- _2 = MaybeUninit::::uninit() -> [return: bb1, unwind: bb6]; // scope 1 at $DIR/check-maybe-uninit.rs:+2:17: +2:44 ++ _2 = MaybeUninit::::uninit() -> [return: bb7, unwind: bb6]; // scope 1 at $DIR/check-maybe-uninit.rs:+2:17: +2:44 + // mir::Constant + // + span: $DIR/check-maybe-uninit.rs:6:17: 6:42 + // + user_ty: UserType(0) + // + literal: Const { ty: fn() -> MaybeUninit {MaybeUninit::::uninit}, val: Value() } + } + + bb1: { + _1 = MaybeUninit::::assume_init(move _2) -> [return: bb2, unwind: bb6]; // scope 1 at $DIR/check-maybe-uninit.rs:+2:17: +2:58 + // mir::Constant + // + span: $DIR/check-maybe-uninit.rs:6:45: 6:56 + // + literal: Const { ty: unsafe fn(MaybeUninit) -> u8 {MaybeUninit::::assume_init}, val: Value() } + } + + bb2: { + StorageDead(_2); // scope 1 at $DIR/check-maybe-uninit.rs:+2:57: +2:58 + StorageDead(_1); // scope 1 at $DIR/check-maybe-uninit.rs:+2:58: +2:59 + StorageLive(_3); // scope 2 at $DIR/check-maybe-uninit.rs:+3:17: +3:62 + StorageLive(_4); // scope 2 at $DIR/check-maybe-uninit.rs:+3:17: +3:48 +- _4 = MaybeUninit::::uninit() -> [return: bb3, unwind: bb6]; // scope 2 at $DIR/check-maybe-uninit.rs:+3:17: +3:48 ++ _4 = MaybeUninit::::uninit() -> [return: bb8, unwind: bb6]; // scope 2 at $DIR/check-maybe-uninit.rs:+3:17: +3:48 + // mir::Constant + // + span: $DIR/check-maybe-uninit.rs:7:17: 7:46 + // + user_ty: UserType(1) + // + literal: Const { ty: fn() -> MaybeUninit {MaybeUninit::::uninit}, val: Value() } + } + + bb3: { + _3 = MaybeUninit::::assume_init(move _4) -> [return: bb4, unwind: bb6]; // scope 2 at $DIR/check-maybe-uninit.rs:+3:17: +3:62 + // mir::Constant + // + span: $DIR/check-maybe-uninit.rs:7:49: 7:60 + // + literal: Const { ty: unsafe fn(MaybeUninit) -> String {MaybeUninit::::assume_init}, val: Value() } + } + + bb4: { + StorageDead(_4); // scope 2 at $DIR/check-maybe-uninit.rs:+3:61: +3:62 + drop(_3) -> [return: bb5, unwind: bb6]; // scope 2 at $DIR/check-maybe-uninit.rs:+3:62: +3:63 + } + + bb5: { + StorageDead(_3); // scope 2 at $DIR/check-maybe-uninit.rs:+3:62: +3:63 + _0 = const (); // scope 1 at $DIR/check-maybe-uninit.rs:+1:5: +4:6 + return; // scope 0 at $DIR/check-maybe-uninit.rs:+5:2: +5:2 + } + + bb6 (cleanup): { + resume; // scope 0 at $DIR/check-maybe-uninit.rs:+0:1: +5:2 ++ } ++ ++ bb7: { ++ _5 = : fn() {assert_uninit_valid_wrapper::}() -> [return: bb1, unwind: bb6]; // scope 0 at $DIR/check-maybe-uninit.rs:+2:45: +2:58 ++ // mir::Constant ++ // + span: $DIR/check-maybe-uninit.rs:6:45: 6:58 ++ // + literal: Const { ty: fn() {assert_uninit_valid_wrapper::}, val: Value(ValTree::Branch(..)) } ++ } ++ ++ bb8: { ++ _6 = : fn() {assert_uninit_valid_wrapper::}() -> [return: bb3, unwind: bb6]; // scope 0 at $DIR/check-maybe-uninit.rs:+3:49: +3:62 ++ // mir::Constant ++ // + span: $DIR/check-maybe-uninit.rs:7:49: 7:62 ++ // + literal: Const { ty: fn() {assert_uninit_valid_wrapper::}, val: Value(ValTree::Branch(..)) } + } + } + diff --git a/tests/mir-opt/check_maybe_uninit.rs b/tests/mir-opt/check_maybe_uninit.rs new file mode 100644 index 0000000000000..4c11dcb49eddb --- /dev/null +++ b/tests/mir-opt/check_maybe_uninit.rs @@ -0,0 +1,9 @@ +use std::mem::MaybeUninit; + +// EMIT_MIR check_maybe_uninit.main.CheckMaybeUninit.diff +fn main() { + unsafe { + let _ = MaybeUninit::::uninit().assume_init(); + let _ = MaybeUninit::::uninit().assume_init(); + } +} diff --git a/tests/ui/consts/assert-type-intrinsics.stderr b/tests/ui/consts/assert-type-intrinsics.stderr index 70aec91e22622..242ae89306ada 100644 --- a/tests/ui/consts/assert-type-intrinsics.stderr +++ b/tests/ui/consts/assert-type-intrinsics.stderr @@ -1,21 +1,75 @@ -error[E0080]: evaluation of constant value failed - --> $DIR/assert-type-intrinsics.rs:12:9 +error: any use of this value will cause an error + --> $DIR/assert-type-intrinsics.rs:14:36 | +LL | const _BAD1: () = unsafe { + | --------------- LL | MaybeUninit::::uninit().assume_init(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to instantiate uninhabited type `!` + | ^^^^^^^^^^^^^ aborted execution: attempted to instantiate uninhabited type `!` + | + = note: `#[deny(const_err)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 -error[E0080]: evaluation of constant value failed - --> $DIR/assert-type-intrinsics.rs:16:9 +error: any use of this value will cause an error + --> $DIR/assert-type-intrinsics.rs:17:9 + | +LL | const _BAD2: () = { + | --------------- +LL | intrinsics::assert_uninit_valid::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `bool` uninitialized, which is invalid | -LL | intrinsics::assert_mem_uninitialized_valid::<&'static i32>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `&i32` uninitialized, which is invalid + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 -error[E0080]: evaluation of constant value failed +error: any use of this value will cause an error --> $DIR/assert-type-intrinsics.rs:20:9 | +LL | const _BAD3: () = { + | --------------- LL | intrinsics::assert_zero_valid::<&'static i32>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to zero-initialize type `&i32`, which is invalid + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0080`. +Future incompatibility report: Future breakage diagnostic: +error: any use of this value will cause an error + --> $DIR/assert-type-intrinsics.rs:14:36 + | +LL | const _BAD1: () = unsafe { + | --------------- +LL | MaybeUninit::::uninit().assume_init(); + | ^^^^^^^^^^^^^ aborted execution: attempted to instantiate uninhabited type `!` + | + = note: `#[deny(const_err)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 + +Future breakage diagnostic: +error: any use of this value will cause an error + --> $DIR/assert-type-intrinsics.rs:17:9 + | +LL | const _BAD2: () = { + | --------------- +LL | intrinsics::assert_uninit_valid::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `bool` uninitialized, which is invalid + | + = note: `#[deny(const_err)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 + +Future breakage diagnostic: +error: any use of this value will cause an error + --> $DIR/assert-type-intrinsics.rs:20:9 + | +LL | const _BAD3: () = { + | --------------- +LL | intrinsics::assert_zero_valid::<&'static i32>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to zero-initialize type `&i32`, which is invalid + | + = note: `#[deny(const_err)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 + diff --git a/tests/ui/intrinsics/maybe_uninit_checks_consts_invalid.rs b/tests/ui/intrinsics/maybe_uninit_checks_consts_invalid.rs new file mode 100644 index 0000000000000..74e50ae48913c --- /dev/null +++ b/tests/ui/intrinsics/maybe_uninit_checks_consts_invalid.rs @@ -0,0 +1,15 @@ +use std::mem::MaybeUninit; + +// Make sure it panics during const eval as well +const X: () = { + unsafe { MaybeUninit::<&'static ()>::uninit().assume_init(); } //~ WARN: the type `&()` does not permit being left uninitialized + unsafe { MaybeUninit::::uninit().assume_init(); } //~ WARN: the type `bool` does not permit being left uninitialized + unsafe { MaybeUninit::::uninit().assume_init(); } //~ WARN: the type `char` does not permit being left uninitialized + unsafe { MaybeUninit::::uninit().assume_init(); } //~ WARN: the type `u8` does not permit being left uninitialized +}; +//~^^^^^ ERROR: any use of this value will cause an error +//~| WARN: this was previously accepted by the compiler but is being phased out + +fn main() { + println!("{X:?}"); +} diff --git a/tests/ui/intrinsics/maybe_uninit_checks_consts_invalid.stderr b/tests/ui/intrinsics/maybe_uninit_checks_consts_invalid.stderr new file mode 100644 index 0000000000000..6ac6f5abcb6d3 --- /dev/null +++ b/tests/ui/intrinsics/maybe_uninit_checks_consts_invalid.stderr @@ -0,0 +1,72 @@ +error: any use of this value will cause an error + --> $DIR/maybe_uninit_checks_consts_invalid.rs:5:51 + | +LL | const X: () = { + | ----------- +LL | unsafe { MaybeUninit::<&'static ()>::uninit().assume_init(); } + | ^^^^^^^^^^^^^ aborted execution: attempted to leave type `&()` uninitialized, which is invalid + | + = note: `#[deny(const_err)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 + +warning: the type `&()` does not permit being left uninitialized + --> $DIR/maybe_uninit_checks_consts_invalid.rs:5:14 + | +LL | unsafe { MaybeUninit::<&'static ()>::uninit().assume_init(); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done + | + = note: `#[warn(invalid_value)]` on by default + = note: references must be non-null + +warning: the type `bool` does not permit being left uninitialized + --> $DIR/maybe_uninit_checks_consts_invalid.rs:6:14 + | +LL | unsafe { MaybeUninit::::uninit().assume_init(); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done + | + = note: booleans must be either `true` or `false` + +warning: the type `char` does not permit being left uninitialized + --> $DIR/maybe_uninit_checks_consts_invalid.rs:7:14 + | +LL | unsafe { MaybeUninit::::uninit().assume_init(); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done + | + = note: characters must be a valid Unicode codepoint + +warning: the type `u8` does not permit being left uninitialized + --> $DIR/maybe_uninit_checks_consts_invalid.rs:8:14 + | +LL | unsafe { MaybeUninit::::uninit().assume_init(); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done + | + = note: integers must not be uninitialized + +error: aborting due to previous error; 4 warnings emitted + +Future incompatibility report: Future breakage diagnostic: +error: any use of this value will cause an error + --> $DIR/maybe_uninit_checks_consts_invalid.rs:5:51 + | +LL | const X: () = { + | ----------- +LL | unsafe { MaybeUninit::<&'static ()>::uninit().assume_init(); } + | ^^^^^^^^^^^^^ aborted execution: attempted to leave type `&()` uninitialized, which is invalid + | + = note: `#[deny(const_err)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 + diff --git a/tests/ui/intrinsics/maybe_uninit_checks_consts_valid.rs b/tests/ui/intrinsics/maybe_uninit_checks_consts_valid.rs new file mode 100644 index 0000000000000..9680a59f2fd2f --- /dev/null +++ b/tests/ui/intrinsics/maybe_uninit_checks_consts_valid.rs @@ -0,0 +1,13 @@ +// run-pass + +use std::mem::MaybeUninit; + +// This test makes sure MU is still usable in consts +const X: () = { + unsafe { MaybeUninit::>::uninit().assume_init(); } + unsafe { MaybeUninit::<()>::uninit().assume_init(); } +}; + +fn main() { + println!("{X:?}"); +} diff --git a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs index 1a0104b859e53..ddc39d33d9607 100644 --- a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -1,19 +1,20 @@ // run-pass -// revisions: default strict +// needs-unwind +// ignore-wasm32-bare compiled with panic=abort by default +// revisions: mir thir strict +// [thir]compile-flags: -Zthir-unsafeck // [strict]compile-flags: -Zstrict-init-checks // ignore-tidy-linelength -// ignore-emscripten spawning processes is not supported -// ignore-sgx no processes -// This test checks panic emitted from `mem::{uninitialized,zeroed}`. +// This test checks panic emitted from `mem::{uninitialized,zeroed}` and `MaybeUninit::{uninit,zeroed}::assume_init`. -#![feature(never_type)] +#![feature(never_type, arbitrary_enum_discriminant)] #![allow(deprecated, invalid_value)] use std::{ - mem::{self, MaybeUninit, ManuallyDrop}, + mem::{uninitialized as mem_uninit, zeroed as mem_zeroed, ManuallyDrop, MaybeUninit}, + num, panic, ptr::NonNull, - num, }; #[allow(dead_code)] @@ -25,7 +26,9 @@ struct Foo { enum Bar {} #[allow(dead_code)] -enum OneVariant { Variant(i32) } +enum OneVariant { + Variant(i32), +} #[allow(dead_code, non_camel_case_types)] enum OneVariant_NonZero { @@ -33,12 +36,6 @@ enum OneVariant_NonZero { DeadVariant(Bar), } -#[allow(dead_code, non_camel_case_types)] -enum OneVariant_Ref { - Variant(&'static i32), - DeadVariant(Bar), -} - // An `Aggregate` abi enum where 0 is not a valid discriminant. #[allow(dead_code)] #[repr(i32)] @@ -68,330 +65,221 @@ enum ZeroIsValid { One(NonNull<()>) = 1, } -#[track_caller] -fn test_panic_msg T) + 'static>(op: F, msg: &str) { - use std::{panic, env, process}; - - // The tricky part is that we can't just run `op`, as that would *abort* the process. - // So instead, we reinvoke this process with the caller location as argument. - // For the purpose of this test, the line number is unique enough. - // If we are running in such a re-invocation, we skip all the tests *except* for the one with that type name. - let our_loc = panic::Location::caller().line().to_string(); - let mut args = env::args(); - let this = args.next().unwrap(); - if let Some(loc) = args.next() { - if loc == our_loc { - op(); - panic!("we did not abort"); - } else { - // Nothing, we are running another test. +fn test_panic_msg(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) { + let err = panic::catch_unwind(op).err(); + assert_eq!(err.as_ref().and_then(|a| a.downcast_ref::<&str>()), Some(&msg)); +} + +macro_rules! test_zero_uninit { + ($zero_fn:ident, $uninit_fn:ident) => { + unsafe { + // Uninhabited types + test_panic_msg( + || $uninit_fn::(), + "attempted to instantiate uninhabited type `!`" + ); + test_panic_msg( + || $zero_fn::(), + "attempted to instantiate uninhabited type `!`" + ); + + test_panic_msg( + || $uninit_fn::(), + "attempted to instantiate uninhabited type `Foo`" + ); + test_panic_msg( + || $zero_fn::(), + "attempted to instantiate uninhabited type `Foo`" + ); + + test_panic_msg( + || $uninit_fn::(), + "attempted to instantiate uninhabited type `Bar`" + ); + test_panic_msg( + || $zero_fn::(), + "attempted to instantiate uninhabited type `Bar`" + ); + + test_panic_msg( + || $uninit_fn::<[Foo; 2]>(), + "attempted to instantiate uninhabited type `[Foo; 2]`" + ); + test_panic_msg( + || $zero_fn::<[Foo; 2]>(), + "attempted to instantiate uninhabited type `[Foo; 2]`" + ); + + test_panic_msg( + || $uninit_fn::<[Bar; 2]>(), + "attempted to instantiate uninhabited type `[Bar; 2]`" + ); + test_panic_msg( + || $zero_fn::<[Bar; 2]>(), + "attempted to instantiate uninhabited type `[Bar; 2]`" + ); + + // Types that do not like zero-initialziation + test_panic_msg( + || $uninit_fn::(), + "attempted to leave type `fn()` uninitialized, which is invalid" + ); + test_panic_msg( + || $zero_fn::(), + "attempted to zero-initialize type `fn()`, which is invalid" + ); + + test_panic_msg( + || $uninit_fn::<*const dyn Send>(), + "attempted to leave type `*const dyn core::marker::Send` uninitialized, which is invalid" + ); + test_panic_msg( + || $zero_fn::<*const dyn Send>(), + "attempted to zero-initialize type `*const dyn core::marker::Send`, which is invalid" + ); + + test_panic_msg( + || $uninit_fn::<(NonNull, u32, u32)>(), + "attempted to leave type `(core::ptr::non_null::NonNull, u32, u32)` uninitialized, \ + which is invalid" + ); + + test_panic_msg( + || $zero_fn::<(NonNull, u32, u32)>(), + "attempted to zero-initialize type `(core::ptr::non_null::NonNull, u32, u32)`, \ + which is invalid" + ); + + test_panic_msg( + || $uninit_fn::(), + "attempted to leave type `OneVariant_NonZero` uninitialized, \ + which is invalid" + ); + test_panic_msg( + || $zero_fn::(), + "attempted to zero-initialize type `OneVariant_NonZero`, \ + which is invalid" + ); + + test_panic_msg( + || $uninit_fn::(), + "attempted to leave type `LR_NonZero` uninitialized, which is invalid" + ); + + test_panic_msg( + || $uninit_fn::>(), + "attempted to leave type `core::mem::manually_drop::ManuallyDrop` uninitialized, \ + which is invalid" + ); + + test_panic_msg( + || $uninit_fn::(), + "attempted to leave type `NoNullVariant` uninitialized, \ + which is invalid" + ); + + test_panic_msg( + || $zero_fn::(), + "attempted to zero-initialize type `NoNullVariant`, \ + which is invalid" + ); + + // Types that can be zero, but not uninit. + test_panic_msg( + || $uninit_fn::(), + "attempted to leave type `bool` uninitialized, which is invalid" + ); + + test_panic_msg( + || $uninit_fn::(), + "attempted to leave type `LR` uninitialized, which is invalid" + ); + + test_panic_msg( + || $uninit_fn::>(), + "attempted to leave type `core::mem::manually_drop::ManuallyDrop` uninitialized, which is invalid" + ); + + // Some things that should work. + let _val = $zero_fn::(); + let _val = $zero_fn::(); + let _val = $zero_fn::>(); + let _val = $zero_fn::(); + let _val = $zero_fn::>(); + let _val = $zero_fn::>>(); + let _val = $zero_fn::<[!; 0]>(); + let _val = $zero_fn::(); + let _val = $uninit_fn::>(); + let _val = $uninit_fn::<[!; 0]>(); + let _val = $uninit_fn::<()>(); + let _val = $uninit_fn::(); + + if cfg!(strict) { + test_panic_msg( + || $uninit_fn::(), + "attempted to leave type `i32` uninitialized, which is invalid" + ); + + test_panic_msg( + || $uninit_fn::<*const ()>(), + "attempted to leave type `*const ()` uninitialized, which is invalid" + ); + + test_panic_msg( + || $uninit_fn::<[i32; 1]>(), + "attempted to leave type `[i32; 1]` uninitialized, which is invalid" + ); + + test_panic_msg( + || $zero_fn::>(), + "attempted to zero-initialize type `core::ptr::non_null::NonNull<()>`, which is invalid" + ); + + test_panic_msg( + || $zero_fn::<[NonNull<()>; 1]>(), + "attempted to zero-initialize type `[core::ptr::non_null::NonNull<()>; 1]`, which is invalid" + ); + + // FIXME(#66151) we conservatively do not error here yet (by default). + test_panic_msg( + || $zero_fn::(), + "attempted to zero-initialize type `LR_NonZero`, which is invalid" + ); + + test_panic_msg( + || $zero_fn::>(), + "attempted to zero-initialize type `core::mem::manually_drop::ManuallyDrop`, \ + which is invalid" + ); + } else { + // These are UB because they have not been officially blessed, but we await the resolution + // of before doing + // anything about that. + let _val = $uninit_fn::(); + let _val = $uninit_fn::<*const ()>(); + + // These are UB, but best to test them to ensure we don't become unintentionally + // stricter. + + // It's currently unchecked to create invalid enums and values inside arrays. + let _val = $zero_fn::(); + let _val = $zero_fn::<[LR_NonZero; 1]>(); + let _val = $zero_fn::<[NonNull<()>; 1]>(); + let _val = $uninit_fn::<[NonNull<()>; 1]>(); + } } - } else { - // Invoke new process for actual test, and check result. - let mut cmd = process::Command::new(this); - cmd.arg(our_loc); - let res = cmd.output().unwrap(); - assert!(!res.status.success(), "test did not fail"); - let stderr = String::from_utf8_lossy(&res.stderr); - assert!(stderr.contains(msg), "test did not contain expected output: looking for {:?}, output:\n{}", msg, stderr); } } -#[track_caller] -fn test_panic_msg_only_if_strict(op: impl (FnOnce() -> T) + 'static, msg: &str) { - if !cfg!(strict) { - // Just run it. - op(); - } else { - test_panic_msg(op, msg); - } +unsafe fn maybe_uninit_uninit() -> T { + MaybeUninit::uninit().assume_init() +} + +unsafe fn maybe_uninit_zeroed() -> T { + MaybeUninit::zeroed().assume_init() } fn main() { - unsafe { - // Uninhabited types - test_panic_msg( - || mem::uninitialized::(), - "attempted to instantiate uninhabited type `!`" - ); - test_panic_msg( - || mem::zeroed::(), - "attempted to instantiate uninhabited type `!`" - ); - test_panic_msg( - || MaybeUninit::::uninit().assume_init(), - "attempted to instantiate uninhabited type `!`" - ); - - test_panic_msg( - || mem::uninitialized::(), - "attempted to instantiate uninhabited type `Foo`" - ); - test_panic_msg( - || mem::zeroed::(), - "attempted to instantiate uninhabited type `Foo`" - ); - test_panic_msg( - || MaybeUninit::::uninit().assume_init(), - "attempted to instantiate uninhabited type `Foo`" - ); - - test_panic_msg( - || mem::uninitialized::(), - "attempted to instantiate uninhabited type `Bar`" - ); - test_panic_msg( - || mem::zeroed::(), - "attempted to instantiate uninhabited type `Bar`" - ); - test_panic_msg( - || MaybeUninit::::uninit().assume_init(), - "attempted to instantiate uninhabited type `Bar`" - ); - - test_panic_msg( - || mem::uninitialized::<[Foo; 2]>(), - "attempted to instantiate uninhabited type `[Foo; 2]`" - ); - test_panic_msg( - || mem::zeroed::<[Foo; 2]>(), - "attempted to instantiate uninhabited type `[Foo; 2]`" - ); - test_panic_msg( - || MaybeUninit::<[Foo; 2]>::uninit().assume_init(), - "attempted to instantiate uninhabited type `[Foo; 2]`" - ); - - test_panic_msg( - || mem::uninitialized::<[Bar; 2]>(), - "attempted to instantiate uninhabited type `[Bar; 2]`" - ); - test_panic_msg( - || mem::zeroed::<[Bar; 2]>(), - "attempted to instantiate uninhabited type `[Bar; 2]`" - ); - test_panic_msg( - || MaybeUninit::<[Bar; 2]>::uninit().assume_init(), - "attempted to instantiate uninhabited type `[Bar; 2]`" - ); - - // Types that don't allow either. - test_panic_msg( - || mem::zeroed::<&i32>(), - "attempted to zero-initialize type `&i32`, which is invalid" - ); - test_panic_msg( - || mem::uninitialized::<&i32>(), - "attempted to leave type `&i32` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::zeroed::>(), - "attempted to zero-initialize type `alloc::boxed::Box<[i32; 0]>`, which is invalid" - ); - test_panic_msg( - || mem::uninitialized::>(), - "attempted to leave type `alloc::boxed::Box<[i32; 0]>` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::zeroed::>(), - "attempted to zero-initialize type `alloc::boxed::Box`, which is invalid" - ); - test_panic_msg( - || mem::uninitialized::>(), - "attempted to leave type `alloc::boxed::Box` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::zeroed::<&[i32]>(), - "attempted to zero-initialize type `&[i32]`, which is invalid" - ); - test_panic_msg( - || mem::uninitialized::<&[i32]>(), - "attempted to leave type `&[i32]` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::zeroed::<&(u8, [u8])>(), - "attempted to zero-initialize type `&(u8, [u8])`, which is invalid" - ); - test_panic_msg( - || mem::uninitialized::<&(u8, [u8])>(), - "attempted to leave type `&(u8, [u8])` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::zeroed::<&dyn Send>(), - "attempted to zero-initialize type `&dyn core::marker::Send`, which is invalid" - ); - test_panic_msg( - || mem::uninitialized::<&dyn Send>(), - "attempted to leave type `&dyn core::marker::Send` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::zeroed::<*const dyn Send>(), - "attempted to zero-initialize type `*const dyn core::marker::Send`, which is invalid" - ); - test_panic_msg( - || mem::uninitialized::<*const dyn Send>(), - "attempted to leave type `*const dyn core::marker::Send` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::uninitialized::(), - "attempted to leave type `NoNullVariant` uninitialized, \ - which is invalid" - ); - test_panic_msg( - || mem::zeroed::(), - "attempted to zero-initialize type `NoNullVariant`, \ - which is invalid" - ); - - test_panic_msg( - || mem::zeroed::(), - "attempted to zero-initialize type `OneVariant_Ref`, \ - which is invalid" - ); - test_panic_msg( - || mem::uninitialized::(), - "attempted to leave type `OneVariant_Ref` uninitialized, which is invalid" - ); - - // Types where both are invalid, but we allow uninit since the 0x01-filling is not LLVM UB. - test_panic_msg( - || mem::zeroed::(), - "attempted to zero-initialize type `fn()`, which is invalid" - ); - test_panic_msg_only_if_strict( - || mem::uninitialized::(), - "attempted to leave type `fn()` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::zeroed::<&()>(), - "attempted to zero-initialize type `&()`, which is invalid" - ); - test_panic_msg_only_if_strict( - || mem::uninitialized::<&()>(), - "attempted to leave type `&()` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::zeroed::<&[u8]>(), - "attempted to zero-initialize type `&[u8]`, which is invalid" - ); - test_panic_msg_only_if_strict( - || mem::uninitialized::<&[u8]>(), - "attempted to leave type `&[u8]` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::zeroed::<&str>(), - "attempted to zero-initialize type `&str`, which is invalid" - ); - test_panic_msg_only_if_strict( - || mem::uninitialized::<&str>(), - "attempted to leave type `&str` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::zeroed::<(NonNull, u32, u32)>(), - "attempted to zero-initialize type `(core::ptr::non_null::NonNull, u32, u32)`, \ - which is invalid" - ); - test_panic_msg_only_if_strict( - || mem::uninitialized::<(NonNull, u32, u32)>(), - "attempted to leave type `(core::ptr::non_null::NonNull, u32, u32)` uninitialized, which is invalid" - ); - - test_panic_msg( - || mem::zeroed::(), - "attempted to zero-initialize type `OneVariant_NonZero`, \ - which is invalid" - ); - test_panic_msg_only_if_strict( - || mem::uninitialized::(), - "attempted to leave type `OneVariant_NonZero` uninitialized, which is invalid" - ); - - // Types where both are invalid but we allow the zeroed form since it is not LLVM UB. - test_panic_msg_only_if_strict( - || mem::zeroed::(), - "attempted to zero-initialize type `LR_NonZero`, which is invalid" - ); - test_panic_msg( - || mem::uninitialized::(), - "attempted to leave type `LR_NonZero` uninitialized, which is invalid" - ); - - test_panic_msg_only_if_strict( - || mem::zeroed::>(), - "attempted to zero-initialize type `core::mem::manually_drop::ManuallyDrop`, \ - which is invalid" - ); - test_panic_msg( - || mem::uninitialized::>(), - "attempted to leave type `core::mem::manually_drop::ManuallyDrop` uninitialized, \ - which is invalid" - ); - - // Some strict-only things - test_panic_msg_only_if_strict( - || mem::uninitialized::(), - "attempted to leave type `i32` uninitialized, which is invalid" - ); - - test_panic_msg_only_if_strict( - || mem::uninitialized::<*const ()>(), - "attempted to leave type `*const ()` uninitialized, which is invalid" - ); - - test_panic_msg_only_if_strict( - || mem::uninitialized::<[i32; 1]>(), - "attempted to leave type `[i32; 1]` uninitialized, which is invalid" - ); - - test_panic_msg_only_if_strict( - || mem::zeroed::<[NonNull<()>; 1]>(), - "attempted to zero-initialize type `[core::ptr::non_null::NonNull<()>; 1]`, which is invalid" - ); - - // Types that can be zero, but not uninit (though some are mitigated). - let _val = mem::zeroed::(); - test_panic_msg( - || mem::uninitialized::(), - "attempted to leave type `LR` uninitialized, which is invalid" - ); - - let _val = mem::zeroed::>(); - test_panic_msg( - || mem::uninitialized::>(), - "attempted to leave type `core::mem::manually_drop::ManuallyDrop` uninitialized, which is invalid" - ); - - let _val = mem::zeroed::(); - test_panic_msg_only_if_strict( - || mem::uninitialized::(), - "attempted to leave type `bool` uninitialized, which is invalid" - ); - - let _val = mem::zeroed::(); - test_panic_msg_only_if_strict( - || mem::uninitialized::(), - "attempted to leave type `OneVariant` uninitialized, which is invalid" - ); - - // Some things that are actually allowed. - let _val = mem::zeroed::>(); - let _val = mem::zeroed::>>(); - let _val = mem::zeroed::<[!; 0]>(); - let _val = mem::zeroed::(); - let _val = mem::uninitialized::>(); - let _val = mem::uninitialized::<[!; 0]>(); - let _val = mem::uninitialized::<()>(); - let _val = mem::uninitialized::(); - } + test_zero_uninit!(mem_zeroed, mem_uninit); + test_zero_uninit!(maybe_uninit_zeroed, maybe_uninit_uninit); }