Auto merge of #99033 - 5225225:interpreter-validity-checks, r=oli-obk

Use constant eval to do strict mem::uninit/zeroed validity checks

I'm not sure about the code organisation here, I just dumped the check in rustc_const_eval at the root. Not hard to move it elsewhere, in any case.

Also, this means cranelift codegen intrinsics lose the strict checks, since they don't seem to depend on rustc_const_eval, and I didn't see a point in keeping around two copies.

I also left comments in the is_zero_valid methods about "uhhh help how do i do this", those apply to both methods equally.

Also rustc_codegen_ssa now depends on rustc_const_eval... is this okay?

Pinging `@RalfJung` since you were the one who mentioned this to me, so I'm assuming you're interested.

Haven't had a chance to run full tests on this since it's really warm, and it's 1AM, I'll check out any failures/comments in the morning :)
This commit is contained in:
bors 2022-07-17 19:28:01 +00:00
commit 263edd43c5
13 changed files with 161 additions and 94 deletions

View File

@ -3752,6 +3752,7 @@ dependencies = [
"rustc_arena", "rustc_arena",
"rustc_ast", "rustc_ast",
"rustc_attr", "rustc_attr",
"rustc_const_eval",
"rustc_data_structures", "rustc_data_structures",
"rustc_errors", "rustc_errors",
"rustc_fs_util", "rustc_fs_util",

View File

@ -58,7 +58,6 @@ pub(crate) use llvm::codegen_llvm_intrinsic_call;
use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::subst::SubstsRef;
use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::symbol::{kw, sym, Symbol};
use rustc_target::abi::InitKind;
use crate::prelude::*; use crate::prelude::*;
use cranelift_codegen::ir::AtomicRmwOp; use cranelift_codegen::ir::AtomicRmwOp;
@ -672,12 +671,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
return; return;
} }
if intrinsic == sym::assert_zero_valid if intrinsic == sym::assert_zero_valid && !fx.tcx.permits_zero_init(layout) {
&& !layout.might_permit_raw_init(
fx,
InitKind::Zero,
fx.tcx.sess.opts.unstable_opts.strict_init_checks) {
with_no_trimmed_paths!({ with_no_trimmed_paths!({
crate::base::codegen_panic( crate::base::codegen_panic(
fx, fx,
@ -688,12 +682,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
return; return;
} }
if intrinsic == sym::assert_uninit_valid if intrinsic == sym::assert_uninit_valid && !fx.tcx.permits_uninit_init(layout) {
&& !layout.might_permit_raw_init(
fx,
InitKind::Uninit,
fx.tcx.sess.opts.unstable_opts.strict_init_checks) {
with_no_trimmed_paths!({ with_no_trimmed_paths!({
crate::base::codegen_panic( crate::base::codegen_panic(
fx, fx,

View File

@ -40,6 +40,7 @@ rustc_metadata = { path = "../rustc_metadata" }
rustc_query_system = { path = "../rustc_query_system" } rustc_query_system = { path = "../rustc_query_system" }
rustc_target = { path = "../rustc_target" } rustc_target = { path = "../rustc_target" }
rustc_session = { path = "../rustc_session" } rustc_session = { path = "../rustc_session" }
rustc_const_eval = { path = "../rustc_const_eval" }
[dependencies.object] [dependencies.object]
version = "0.29.0" version = "0.29.0"

View File

@ -22,7 +22,7 @@ use rustc_span::source_map::Span;
use rustc_span::{sym, Symbol}; use rustc_span::{sym, Symbol};
use rustc_symbol_mangling::typeid_for_fnabi; use rustc_symbol_mangling::typeid_for_fnabi;
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
use rustc_target::abi::{self, HasDataLayout, InitKind, WrappingRange}; use rustc_target::abi::{self, HasDataLayout, WrappingRange};
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
/// Used by `FunctionCx::codegen_terminator` for emitting common patterns /// Used by `FunctionCx::codegen_terminator` for emitting common patterns
@ -528,7 +528,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
source_info: mir::SourceInfo, source_info: mir::SourceInfo,
target: Option<mir::BasicBlock>, target: Option<mir::BasicBlock>,
cleanup: Option<mir::BasicBlock>, cleanup: Option<mir::BasicBlock>,
strict_validity: bool,
) -> bool { ) -> bool {
// Emit a panic or a no-op for `assert_*` intrinsics. // Emit a panic or a no-op for `assert_*` intrinsics.
// These are intrinsics that compile to panics so that we can get a message // These are intrinsics that compile to panics so that we can get a message
@ -547,12 +546,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}); });
if let Some(intrinsic) = panic_intrinsic { if let Some(intrinsic) = panic_intrinsic {
use AssertIntrinsic::*; use AssertIntrinsic::*;
let ty = instance.unwrap().substs.type_at(0); let ty = instance.unwrap().substs.type_at(0);
let layout = bx.layout_of(ty); let layout = bx.layout_of(ty);
let do_panic = match intrinsic { let do_panic = match intrinsic {
Inhabited => layout.abi.is_uninhabited(), Inhabited => layout.abi.is_uninhabited(),
ZeroValid => !layout.might_permit_raw_init(bx, InitKind::Zero, strict_validity), ZeroValid => !bx.tcx().permits_zero_init(layout),
UninitValid => !layout.might_permit_raw_init(bx, InitKind::Uninit, strict_validity), UninitValid => !bx.tcx().permits_uninit_init(layout),
}; };
if do_panic { if do_panic {
let msg_str = with_no_visible_paths!({ let msg_str = with_no_visible_paths!({
@ -687,7 +687,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
source_info, source_info,
target, target,
cleanup, cleanup,
self.cx.tcx().sess.opts.unstable_opts.strict_init_checks,
) { ) {
return; return;
} }

View File

@ -104,7 +104,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
} }
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
pub(super) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self { pub(crate) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self {
CompileTimeInterpreter { CompileTimeInterpreter {
steps_remaining: const_eval_limit.0, steps_remaining: const_eval_limit.0,
stack: Vec::new(), stack: Vec::new(),

View File

@ -15,7 +15,7 @@ use rustc_middle::ty::layout::LayoutOf as _;
use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::ty::{Ty, TyCtxt};
use rustc_span::symbol::{sym, Symbol}; use rustc_span::symbol::{sym, Symbol};
use rustc_target::abi::{Abi, Align, InitKind, Primitive, Size}; use rustc_target::abi::{Abi, Align, Primitive, Size};
use super::{ use super::{
util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy, util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
@ -411,35 +411,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
), ),
)?; )?;
} }
if intrinsic_name == sym::assert_zero_valid
&& !layout.might_permit_raw_init( if intrinsic_name == sym::assert_zero_valid {
self, let should_panic = !self.tcx.permits_zero_init(layout);
InitKind::Zero,
self.tcx.sess.opts.unstable_opts.strict_init_checks, if should_panic {
) M::abort(
{ self,
M::abort( format!(
self, "aborted execution: attempted to zero-initialize type `{}`, which is invalid",
format!( ty
"aborted execution: attempted to zero-initialize type `{}`, which is invalid", ),
ty )?;
), }
)?;
} }
if intrinsic_name == sym::assert_uninit_valid
&& !layout.might_permit_raw_init( if intrinsic_name == sym::assert_uninit_valid {
self, let should_panic = !self.tcx.permits_uninit_init(layout);
InitKind::Uninit,
self.tcx.sess.opts.unstable_opts.strict_init_checks, if should_panic {
) M::abort(
{ self,
M::abort( format!(
self, "aborted execution: attempted to leave type `{}` uninitialized, which is invalid",
format!( ty
"aborted execution: attempted to leave type `{}` uninitialized, which is invalid", ),
ty )?;
), }
)?;
} }
} }
sym::simd_insert => { sym::simd_insert => {

View File

@ -33,11 +33,13 @@ extern crate rustc_middle;
pub mod const_eval; pub mod const_eval;
mod errors; mod errors;
pub mod interpret; pub mod interpret;
mod might_permit_raw_init;
pub mod transform; pub mod transform;
pub mod util; pub mod util;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_middle::ty::query::Providers; use rustc_middle::ty::query::Providers;
use rustc_target::abi::InitKind;
pub fn provide(providers: &mut Providers) { pub fn provide(providers: &mut Providers) {
const_eval::provide(providers); const_eval::provide(providers);
@ -59,4 +61,8 @@ pub fn provide(providers: &mut Providers) {
let (param_env, value) = param_env_and_value.into_parts(); let (param_env, value) = param_env_and_value.into_parts();
const_eval::deref_mir_constant(tcx, param_env, value) const_eval::deref_mir_constant(tcx, param_env, value)
}; };
providers.permits_uninit_init =
|tcx, ty| might_permit_raw_init::might_permit_raw_init(tcx, ty, InitKind::Uninit);
providers.permits_zero_init =
|tcx, ty| might_permit_raw_init::might_permit_raw_init(tcx, ty, InitKind::Zero);
} }

View File

@ -0,0 +1,40 @@
use crate::const_eval::CompileTimeInterpreter;
use crate::interpret::{InterpCx, MemoryKind, OpTy};
use rustc_middle::ty::layout::LayoutCx;
use rustc_middle::ty::{layout::TyAndLayout, ParamEnv, TyCtxt};
use rustc_session::Limit;
use rustc_target::abi::InitKind;
pub fn might_permit_raw_init<'tcx>(
tcx: TyCtxt<'tcx>,
ty: TyAndLayout<'tcx>,
kind: InitKind,
) -> bool {
let strict = tcx.sess.opts.unstable_opts.strict_init_checks;
if strict {
let machine = CompileTimeInterpreter::new(Limit::new(0), false);
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
let allocated = cx
.allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
.expect("OOM: failed to allocate for uninit check");
if kind == InitKind::Zero {
cx.write_bytes_ptr(
allocated.ptr,
std::iter::repeat(0_u8).take(ty.layout.size().bytes_usize()),
)
.expect("failed to write bytes for zero valid check");
}
let ot: OpTy<'_, _> = allocated.into();
// Assume that if it failed, it's a validation failure.
cx.validate_operand(&ot).is_ok()
} else {
let layout_cx = LayoutCx { tcx, param_env: ParamEnv::reveal_all() };
ty.might_permit_raw_init(&layout_cx, kind)
}
}

View File

@ -2053,4 +2053,12 @@ rustc_queries! {
desc { |tcx| "looking up generator diagnostic data of `{}`", tcx.def_path_str(key) } desc { |tcx| "looking up generator diagnostic data of `{}`", tcx.def_path_str(key) }
separate_provide_extern separate_provide_extern
} }
query permits_uninit_init(key: TyAndLayout<'tcx>) -> bool {
desc { "checking to see if {:?} permits being left uninit", key.ty }
}
query permits_zero_init(key: TyAndLayout<'tcx>) -> bool {
desc { "checking to see if {:?} permits being left zeroed", key.ty }
}
} }

View File

@ -28,6 +28,7 @@ use crate::traits::query::{
use crate::traits::specialization_graph; use crate::traits::specialization_graph;
use crate::traits::{self, ImplSource}; use crate::traits::{self, ImplSource};
use crate::ty::fast_reject::SimplifiedType; use crate::ty::fast_reject::SimplifiedType;
use crate::ty::layout::TyAndLayout;
use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::subst::{GenericArg, SubstsRef};
use crate::ty::util::AlwaysRequiresDrop; use crate::ty::util::AlwaysRequiresDrop;
use crate::ty::GeneratorDiagnosticData; use crate::ty::GeneratorDiagnosticData;

View File

@ -6,7 +6,7 @@ use rustc_middle::mir;
use rustc_middle::traits; use rustc_middle::traits;
use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::subst::{GenericArg, SubstsRef}; use rustc_middle::ty::subst::{GenericArg, SubstsRef};
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt};
use rustc_span::symbol::{Ident, Symbol}; use rustc_span::symbol::{Ident, Symbol};
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
@ -385,6 +385,16 @@ impl<'tcx> Key for Ty<'tcx> {
} }
} }
impl<'tcx> Key for TyAndLayout<'tcx> {
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
fn default_span(&self, _: TyCtxt<'_>) -> Span {
DUMMY_SP
}
}
impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) { impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) {
#[inline(always)] #[inline(always)]
fn query_crate_is_local(&self) -> bool { fn query_crate_is_local(&self) -> bool {

View File

@ -1372,7 +1372,7 @@ pub struct PointeeInfo {
/// Used in `might_permit_raw_init` to indicate the kind of initialisation /// Used in `might_permit_raw_init` to indicate the kind of initialisation
/// that is checked to be valid /// that is checked to be valid
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InitKind { pub enum InitKind {
Zero, Zero,
Uninit, Uninit,
@ -1487,14 +1487,18 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
/// ///
/// `init_kind` indicates if the memory is zero-initialized or left uninitialized. /// `init_kind` indicates if the memory is zero-initialized or left uninitialized.
/// ///
/// `strict` is an opt-in debugging flag added in #97323 that enables more checks. /// This code is intentionally conservative, and will not detect
/// * zero init of an enum whose 0 variant does not allow zero initialization
/// * making uninitialized types who have a full valid range (ints, floats, raw pointers)
/// * Any form of invalid value being made inside an array (unless the value is uninhabited)
/// ///
/// This is conservative: in doubt, it will answer `true`. /// A strict form of these checks that uses const evaluation exists in
/// `rustc_const_eval::might_permit_raw_init`, and a tracking issue for making these checks
/// stricter is <https://github.com/rust-lang/rust/issues/66151>.
/// ///
/// FIXME: Once we removed all the conservatism, we could alternatively /// FIXME: Once all the conservatism is removed from here, and the checks are ran by default,
/// create an all-0/all-undef constant and run the const value validator to see if /// we can use the const evaluation checks always instead.
/// this is a valid value for the given type. pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind) -> bool
pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind, strict: bool) -> bool
where where
Self: Copy, Self: Copy,
Ty: TyAbiInterface<'a, C>, Ty: TyAbiInterface<'a, C>,
@ -1507,13 +1511,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
s.valid_range(cx).contains(0) s.valid_range(cx).contains(0)
} }
InitKind::Uninit => { InitKind::Uninit => {
if strict { // The range must include all values.
// The type must be allowed to be uninit (which means "is a union"). s.is_always_valid(cx)
s.is_uninit_valid()
} else {
// The range must include all values.
s.is_always_valid(cx)
}
} }
} }
}; };
@ -1534,19 +1533,12 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
// If we have not found an error yet, we need to recursively descend into fields. // If we have not found an error yet, we need to recursively descend into fields.
match &self.fields { match &self.fields {
FieldsShape::Primitive | FieldsShape::Union { .. } => {} FieldsShape::Primitive | FieldsShape::Union { .. } => {}
FieldsShape::Array { count, .. } => { FieldsShape::Array { .. } => {
// FIXME(#66151): For now, we are conservative and do not check arrays by default. // FIXME(#66151): For now, we are conservative and do not check arrays by default.
if strict
&& *count > 0
&& !self.field(cx, 0).might_permit_raw_init(cx, init_kind, strict)
{
// Found non empty array with a type that is unhappy about this kind of initialization
return false;
}
} }
FieldsShape::Arbitrary { offsets, .. } => { FieldsShape::Arbitrary { offsets, .. } => {
for idx in 0..offsets.len() { for idx in 0..offsets.len() {
if !self.field(cx, idx).might_permit_raw_init(cx, init_kind, strict) { if !self.field(cx, idx).might_permit_raw_init(cx, init_kind) {
// We found a field that is unhappy with this kind of initialization. // We found a field that is unhappy with this kind of initialization.
return false; return false;
} }

View File

@ -57,6 +57,13 @@ enum LR_NonZero {
struct ZeroSized; struct ZeroSized;
#[allow(dead_code)]
#[repr(i32)]
enum ZeroIsValid {
Zero(u8) = 0,
One(NonNull<()>) = 1,
}
fn test_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) { fn test_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
let err = panic::catch_unwind(op).err(); let err = panic::catch_unwind(op).err();
assert_eq!( assert_eq!(
@ -152,33 +159,12 @@ fn main() {
"attempted to zero-initialize type `*const dyn core::marker::Send`, which is invalid" "attempted to zero-initialize type `*const dyn core::marker::Send`, which is invalid"
); );
/* FIXME(#66151) we conservatively do not error here yet.
test_panic_msg(
|| mem::uninitialized::<LR_NonZero>(),
"attempted to leave type `LR_NonZero` uninitialized, which is invalid"
);
test_panic_msg(
|| mem::zeroed::<LR_NonZero>(),
"attempted to zero-initialize type `LR_NonZero`, which is invalid"
);
test_panic_msg(
|| mem::uninitialized::<ManuallyDrop<LR_NonZero>>(),
"attempted to leave type `std::mem::ManuallyDrop<LR_NonZero>` uninitialized, \
which is invalid"
);
test_panic_msg(
|| mem::zeroed::<ManuallyDrop<LR_NonZero>>(),
"attempted to zero-initialize type `std::mem::ManuallyDrop<LR_NonZero>`, \
which is invalid"
);
*/
test_panic_msg( test_panic_msg(
|| mem::uninitialized::<(NonNull<u32>, u32, u32)>(), || mem::uninitialized::<(NonNull<u32>, u32, u32)>(),
"attempted to leave type `(core::ptr::non_null::NonNull<u32>, u32, u32)` uninitialized, \ "attempted to leave type `(core::ptr::non_null::NonNull<u32>, u32, u32)` uninitialized, \
which is invalid" which is invalid"
); );
test_panic_msg( test_panic_msg(
|| mem::zeroed::<(NonNull<u32>, u32, u32)>(), || mem::zeroed::<(NonNull<u32>, u32, u32)>(),
"attempted to zero-initialize type `(core::ptr::non_null::NonNull<u32>, u32, u32)`, \ "attempted to zero-initialize type `(core::ptr::non_null::NonNull<u32>, u32, u32)`, \
@ -196,11 +182,23 @@ fn main() {
which is invalid" which is invalid"
); );
test_panic_msg(
|| mem::uninitialized::<LR_NonZero>(),
"attempted to leave type `LR_NonZero` uninitialized, which is invalid"
);
test_panic_msg(
|| mem::uninitialized::<ManuallyDrop<LR_NonZero>>(),
"attempted to leave type `core::mem::manually_drop::ManuallyDrop<LR_NonZero>` uninitialized, \
which is invalid"
);
test_panic_msg( test_panic_msg(
|| mem::uninitialized::<NoNullVariant>(), || mem::uninitialized::<NoNullVariant>(),
"attempted to leave type `NoNullVariant` uninitialized, \ "attempted to leave type `NoNullVariant` uninitialized, \
which is invalid" which is invalid"
); );
test_panic_msg( test_panic_msg(
|| mem::zeroed::<NoNullVariant>(), || mem::zeroed::<NoNullVariant>(),
"attempted to zero-initialize type `NoNullVariant`, \ "attempted to zero-initialize type `NoNullVariant`, \
@ -212,10 +210,12 @@ fn main() {
|| mem::uninitialized::<bool>(), || mem::uninitialized::<bool>(),
"attempted to leave type `bool` uninitialized, which is invalid" "attempted to leave type `bool` uninitialized, which is invalid"
); );
test_panic_msg( test_panic_msg(
|| mem::uninitialized::<LR>(), || mem::uninitialized::<LR>(),
"attempted to leave type `LR` uninitialized, which is invalid" "attempted to leave type `LR` uninitialized, which is invalid"
); );
test_panic_msg( test_panic_msg(
|| mem::uninitialized::<ManuallyDrop<LR>>(), || mem::uninitialized::<ManuallyDrop<LR>>(),
"attempted to leave type `core::mem::manually_drop::ManuallyDrop<LR>` uninitialized, which is invalid" "attempted to leave type `core::mem::manually_drop::ManuallyDrop<LR>` uninitialized, which is invalid"
@ -229,6 +229,7 @@ fn main() {
let _val = mem::zeroed::<Option<&'static i32>>(); let _val = mem::zeroed::<Option<&'static i32>>();
let _val = mem::zeroed::<MaybeUninit<NonNull<u32>>>(); let _val = mem::zeroed::<MaybeUninit<NonNull<u32>>>();
let _val = mem::zeroed::<[!; 0]>(); let _val = mem::zeroed::<[!; 0]>();
let _val = mem::zeroed::<ZeroIsValid>();
let _val = mem::uninitialized::<MaybeUninit<bool>>(); let _val = mem::uninitialized::<MaybeUninit<bool>>();
let _val = mem::uninitialized::<[!; 0]>(); let _val = mem::uninitialized::<[!; 0]>();
let _val = mem::uninitialized::<()>(); let _val = mem::uninitialized::<()>();
@ -259,12 +260,33 @@ fn main() {
|| mem::zeroed::<[NonNull<()>; 1]>(), || mem::zeroed::<[NonNull<()>; 1]>(),
"attempted to zero-initialize type `[core::ptr::non_null::NonNull<()>; 1]`, which is invalid" "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(
|| mem::zeroed::<LR_NonZero>(),
"attempted to zero-initialize type `LR_NonZero`, which is invalid"
);
test_panic_msg(
|| mem::zeroed::<ManuallyDrop<LR_NonZero>>(),
"attempted to zero-initialize type `core::mem::manually_drop::ManuallyDrop<LR_NonZero>`, \
which is invalid"
);
} else { } else {
// These are UB because they have not been officially blessed, but we await the resolution // These are UB because they have not been officially blessed, but we await the resolution
// of <https://github.com/rust-lang/unsafe-code-guidelines/issues/71> before doing // of <https://github.com/rust-lang/unsafe-code-guidelines/issues/71> before doing
// anything about that. // anything about that.
let _val = mem::uninitialized::<i32>(); let _val = mem::uninitialized::<i32>();
let _val = mem::uninitialized::<*const ()>(); let _val = mem::uninitialized::<*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 = mem::zeroed::<LR_NonZero>();
let _val = mem::zeroed::<[LR_NonZero; 1]>();
let _val = mem::zeroed::<[NonNull<()>; 1]>();
let _val = mem::uninitialized::<[NonNull<()>; 1]>();
} }
} }
} }