Rollup merge of #66059 - RalfJung:panic-on-non-zero, r=eddyb
mem::zeroed/uninit: panic on types that do not permit zero-initialization r? @eddyb @oli-obk Cc https://github.com/rust-lang/rust/issues/62825 Also see [this summary comment](https://github.com/rust-lang/rust/pull/66059#issuecomment-586734747)
This commit is contained in:
commit
a7c2eef2ae
@ -1007,6 +1007,16 @@ extern "rust-intrinsic" {
|
||||
/// This will statically either panic, or do nothing.
|
||||
pub fn panic_if_uninhabited<T>();
|
||||
|
||||
/// A guard for unsafe functions that cannot ever be executed if `T` does not permit
|
||||
/// zero-initialization: This will statically either panic, or do nothing.
|
||||
#[cfg(not(bootstrap))]
|
||||
pub fn panic_if_zero_invalid<T>();
|
||||
|
||||
/// A guard for unsafe functions that cannot ever be executed if `T` has invalid
|
||||
/// bit patterns: This will statically either panic, or do nothing.
|
||||
#[cfg(not(bootstrap))]
|
||||
pub fn panic_if_any_invalid<T>();
|
||||
|
||||
/// Gets a reference to a static `Location` indicating where it was called.
|
||||
#[rustc_const_unstable(feature = "const_caller_location", issue = "47809")]
|
||||
pub fn caller_location() -> &'static crate::panic::Location<'static>;
|
||||
|
@ -496,6 +496,9 @@ pub const fn needs_drop<T>() -> bool {
|
||||
#[allow(deprecated)]
|
||||
#[rustc_diagnostic_item = "mem_zeroed"]
|
||||
pub unsafe fn zeroed<T>() -> T {
|
||||
#[cfg(not(bootstrap))]
|
||||
intrinsics::panic_if_zero_invalid::<T>();
|
||||
#[cfg(bootstrap)]
|
||||
intrinsics::panic_if_uninhabited::<T>();
|
||||
intrinsics::init()
|
||||
}
|
||||
@ -529,6 +532,9 @@ pub unsafe fn zeroed<T>() -> T {
|
||||
#[allow(deprecated)]
|
||||
#[rustc_diagnostic_item = "mem_uninitialized"]
|
||||
pub unsafe fn uninitialized<T>() -> T {
|
||||
#[cfg(not(bootstrap))]
|
||||
intrinsics::panic_if_any_invalid::<T>();
|
||||
#[cfg(bootstrap)]
|
||||
intrinsics::panic_if_uninhabited::<T>();
|
||||
intrinsics::uninit()
|
||||
}
|
||||
|
@ -1904,36 +1904,6 @@ impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MaybeResult<T> {
|
||||
type Error;
|
||||
|
||||
fn from(x: Result<T, Self::Error>) -> Self;
|
||||
fn to_result(self) -> Result<T, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> MaybeResult<T> for T {
|
||||
type Error = !;
|
||||
|
||||
fn from(x: Result<T, Self::Error>) -> Self {
|
||||
let Ok(x) = x;
|
||||
x
|
||||
}
|
||||
fn to_result(self) -> Result<T, Self::Error> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> MaybeResult<T> for Result<T, E> {
|
||||
type Error = E;
|
||||
|
||||
fn from(x: Result<T, Self::Error>) -> Self {
|
||||
x
|
||||
}
|
||||
fn to_result(self) -> Result<T, Self::Error> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub type TyLayout<'tcx> = ::rustc_target::abi::TyLayout<'tcx, Ty<'tcx>>;
|
||||
|
||||
impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||
|
@ -434,6 +434,89 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup);
|
||||
}
|
||||
|
||||
/// Returns `true` if this is indeed a panic intrinsic and codegen is done.
|
||||
fn codegen_panic_intrinsic(
|
||||
&mut self,
|
||||
helper: &TerminatorCodegenHelper<'tcx>,
|
||||
bx: &mut Bx,
|
||||
intrinsic: Option<&str>,
|
||||
instance: Option<Instance<'tcx>>,
|
||||
span: Span,
|
||||
destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>,
|
||||
cleanup: Option<mir::BasicBlock>,
|
||||
) -> bool {
|
||||
// Emit a panic or a no-op for `panic_if_uninhabited`.
|
||||
// These are intrinsics that compile to panics so that we can get a message
|
||||
// which mentions the offending type, even from a const context.
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum PanicIntrinsic {
|
||||
IfUninhabited,
|
||||
IfZeroInvalid,
|
||||
IfAnyInvalid,
|
||||
};
|
||||
let panic_intrinsic = intrinsic.and_then(|i| match i {
|
||||
// FIXME: Move to symbols instead of strings.
|
||||
"panic_if_uninhabited" => Some(PanicIntrinsic::IfUninhabited),
|
||||
"panic_if_zero_invalid" => Some(PanicIntrinsic::IfZeroInvalid),
|
||||
"panic_if_any_invalid" => Some(PanicIntrinsic::IfAnyInvalid),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(intrinsic) = panic_intrinsic {
|
||||
use PanicIntrinsic::*;
|
||||
let ty = instance.unwrap().substs.type_at(0);
|
||||
let layout = bx.layout_of(ty);
|
||||
let do_panic = match intrinsic {
|
||||
IfUninhabited => layout.abi.is_uninhabited(),
|
||||
// We unwrap as the error type is `!`.
|
||||
IfZeroInvalid => !layout.might_permit_raw_init(bx, /*zero:*/ true).unwrap(),
|
||||
// We unwrap as the error type is `!`.
|
||||
IfAnyInvalid => !layout.might_permit_raw_init(bx, /*zero:*/ false).unwrap(),
|
||||
};
|
||||
if do_panic {
|
||||
let msg_str = if layout.abi.is_uninhabited() {
|
||||
// Use this error even for the other intrinsics as it is more precise.
|
||||
format!("attempted to instantiate uninhabited type `{}`", ty)
|
||||
} else if intrinsic == IfZeroInvalid {
|
||||
format!("attempted to zero-initialize type `{}`, which is invalid", ty)
|
||||
} else {
|
||||
format!("attempted to leave type `{}` uninitialized, which is invalid", ty)
|
||||
};
|
||||
let msg = bx.const_str(Symbol::intern(&msg_str));
|
||||
let location = self.get_caller_location(bx, span).immediate();
|
||||
|
||||
// Obtain the panic entry point.
|
||||
// FIXME: dedup this with `codegen_assert_terminator` above.
|
||||
let def_id =
|
||||
common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
|
||||
let instance = ty::Instance::mono(bx.tcx(), def_id);
|
||||
let fn_abi = FnAbi::of_instance(bx, instance, &[]);
|
||||
let llfn = bx.get_fn_addr(instance);
|
||||
|
||||
if let Some((_, target)) = destination.as_ref() {
|
||||
helper.maybe_sideeffect(self.mir, bx, &[*target]);
|
||||
}
|
||||
// Codegen the actual panic invoke/call.
|
||||
helper.do_call(
|
||||
self,
|
||||
bx,
|
||||
fn_abi,
|
||||
llfn,
|
||||
&[msg.0, msg.1, location],
|
||||
destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
|
||||
cleanup,
|
||||
);
|
||||
} else {
|
||||
// a NOP
|
||||
let target = destination.as_ref().unwrap().1;
|
||||
helper.maybe_sideeffect(self.mir, bx, &[target]);
|
||||
helper.funclet_br(self, bx, target)
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn codegen_call_terminator(
|
||||
&mut self,
|
||||
helper: TerminatorCodegenHelper<'tcx>,
|
||||
@ -520,41 +603,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
bug!("`miri_start_panic` should never end up in compiled code");
|
||||
}
|
||||
|
||||
// Emit a panic or a no-op for `panic_if_uninhabited`.
|
||||
if intrinsic == Some("panic_if_uninhabited") {
|
||||
let ty = instance.unwrap().substs.type_at(0);
|
||||
let layout = bx.layout_of(ty);
|
||||
if layout.abi.is_uninhabited() {
|
||||
let msg_str = format!("Attempted to instantiate uninhabited type {}", ty);
|
||||
let msg = bx.const_str(Symbol::intern(&msg_str));
|
||||
let location = self.get_caller_location(&mut bx, span).immediate();
|
||||
|
||||
// Obtain the panic entry point.
|
||||
let def_id =
|
||||
common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
|
||||
let instance = ty::Instance::mono(bx.tcx(), def_id);
|
||||
let fn_abi = FnAbi::of_instance(&bx, instance, &[]);
|
||||
let llfn = bx.get_fn_addr(instance);
|
||||
|
||||
if let Some((_, target)) = destination.as_ref() {
|
||||
helper.maybe_sideeffect(self.mir, &mut bx, &[*target]);
|
||||
}
|
||||
// Codegen the actual panic invoke/call.
|
||||
helper.do_call(
|
||||
self,
|
||||
&mut bx,
|
||||
fn_abi,
|
||||
llfn,
|
||||
&[msg.0, msg.1, location],
|
||||
destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
|
||||
cleanup,
|
||||
);
|
||||
} else {
|
||||
// a NOP
|
||||
let target = destination.as_ref().unwrap().1;
|
||||
helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
|
||||
helper.funclet_br(self, &mut bx, target)
|
||||
}
|
||||
if self.codegen_panic_intrinsic(
|
||||
&helper,
|
||||
&mut bx,
|
||||
intrinsic,
|
||||
instance,
|
||||
span,
|
||||
destination,
|
||||
cleanup,
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ macro_rules! newtype_index {
|
||||
|
||||
#[inline]
|
||||
fn index(self) -> usize {
|
||||
usize::from(self)
|
||||
self.as_usize()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -937,6 +937,7 @@ impl<'a, Ty> Deref for TyLayout<'a, Ty> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for context types that can compute layouts of things.
|
||||
pub trait LayoutOf {
|
||||
type Ty;
|
||||
type TyLayout;
|
||||
@ -947,6 +948,38 @@ pub trait LayoutOf {
|
||||
}
|
||||
}
|
||||
|
||||
/// The `TyLayout` above will always be a `MaybeResult<TyLayout<'_, Self>>`.
|
||||
/// We can't add the bound due to the lifetime, but this trait is still useful when
|
||||
/// writing code that's generic over the `LayoutOf` impl.
|
||||
pub trait MaybeResult<T> {
|
||||
type Error;
|
||||
|
||||
fn from(x: Result<T, Self::Error>) -> Self;
|
||||
fn to_result(self) -> Result<T, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> MaybeResult<T> for T {
|
||||
type Error = !;
|
||||
|
||||
fn from(Ok(x): Result<T, Self::Error>) -> Self {
|
||||
x
|
||||
}
|
||||
fn to_result(self) -> Result<T, Self::Error> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> MaybeResult<T> for Result<T, E> {
|
||||
type Error = E;
|
||||
|
||||
fn from(x: Result<T, Self::Error>) -> Self {
|
||||
x
|
||||
}
|
||||
fn to_result(self) -> Result<T, Self::Error> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum PointerKind {
|
||||
/// Most general case, we know no restrictions to tell LLVM.
|
||||
@ -987,6 +1020,9 @@ impl<'a, Ty> TyLayout<'a, Ty> {
|
||||
{
|
||||
Ty::for_variant(self, cx, variant_index)
|
||||
}
|
||||
|
||||
/// Callers might want to use `C: LayoutOf<Ty=Ty, TyLayout: MaybeResult<Self>>`
|
||||
/// to allow recursion (see `might_permit_zero_init` below for an example).
|
||||
pub fn field<C>(self, cx: &C, i: usize) -> C::TyLayout
|
||||
where
|
||||
Ty: TyLayoutMethods<'a, C>,
|
||||
@ -994,6 +1030,7 @@ impl<'a, Ty> TyLayout<'a, Ty> {
|
||||
{
|
||||
Ty::field(self, cx, i)
|
||||
}
|
||||
|
||||
pub fn pointee_info_at<C>(self, cx: &C, offset: Size) -> Option<PointeeInfo>
|
||||
where
|
||||
Ty: TyLayoutMethods<'a, C>,
|
||||
@ -1017,4 +1054,52 @@ impl<'a, Ty> TyLayout<'a, Ty> {
|
||||
Abi::Aggregate { sized } => sized && self.size.bytes() == 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if this type permits "raw" initialization by just transmuting some
|
||||
/// memory into an instance of `T`.
|
||||
/// `zero` indicates if the memory is zero-initialized, or alternatively
|
||||
/// left entirely uninitialized.
|
||||
/// This is conservative: in doubt, it will answer `true`.
|
||||
///
|
||||
/// FIXME: Once we removed all the conservatism, we could alternatively
|
||||
/// create an all-0/all-undef constant and run the const value validator to see if
|
||||
/// this is a valid value for the given type.
|
||||
pub fn might_permit_raw_init<C, E>(self, cx: &C, zero: bool) -> Result<bool, E>
|
||||
where
|
||||
Self: Copy,
|
||||
Ty: TyLayoutMethods<'a, C>,
|
||||
C: LayoutOf<Ty = Ty, TyLayout: MaybeResult<Self, Error = E>> + HasDataLayout,
|
||||
{
|
||||
let scalar_allows_raw_init = move |s: &Scalar| -> bool {
|
||||
if zero {
|
||||
let range = &s.valid_range;
|
||||
// The range must contain 0.
|
||||
range.contains(&0) || (*range.start() > *range.end()) // wrap-around allows 0
|
||||
} else {
|
||||
// The range must include all values. `valid_range_exclusive` handles
|
||||
// the wrap-around using target arithmetic; with wrap-around then the full
|
||||
// range is one where `start == end`.
|
||||
let range = s.valid_range_exclusive(cx);
|
||||
range.start == range.end
|
||||
}
|
||||
};
|
||||
|
||||
// Check the ABI.
|
||||
let valid = match &self.abi {
|
||||
Abi::Uninhabited => false, // definitely UB
|
||||
Abi::Scalar(s) => scalar_allows_raw_init(s),
|
||||
Abi::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2),
|
||||
Abi::Vector { element: s, count } => *count == 0 || scalar_allows_raw_init(s),
|
||||
Abi::Aggregate { .. } => true, // Cannot be excluded *right now*.
|
||||
};
|
||||
if !valid {
|
||||
// This is definitely not okay.
|
||||
trace!("might_permit_raw_init({:?}, zero={}): not valid", self.details, zero);
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// If we have not found an error yet, we need to recursively descend.
|
||||
// FIXME(#66151): For now, we are conservative and do not do this.
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,9 @@
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
|
||||
#![feature(bool_to_option)]
|
||||
#![feature(nll)]
|
||||
#![feature(never_type)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(exhaustive_patterns)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
@ -147,7 +147,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
|
||||
),
|
||||
"rustc_peek" => (1, vec![param(0)], param(0)),
|
||||
"caller_location" => (0, vec![], tcx.caller_location_ty()),
|
||||
"panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()),
|
||||
"panic_if_uninhabited" | "panic_if_zero_invalid" | "panic_if_any_invalid" => {
|
||||
(1, Vec::new(), tcx.mk_unit())
|
||||
}
|
||||
"init" => (1, Vec::new(), param(0)),
|
||||
"uninit" => (1, Vec::new(), param(0)),
|
||||
"forget" => (1, vec![param(0)], tcx.mk_unit()),
|
||||
|
170
src/test/ui/intrinsics/panic-uninitialized-zeroed.rs
Normal file
170
src/test/ui/intrinsics/panic-uninitialized-zeroed.rs
Normal file
@ -0,0 +1,170 @@
|
||||
// run-pass
|
||||
// ignore-wasm32-bare compiled with panic=abort by default
|
||||
|
||||
// This test checks panic emitted from `mem::{uninitialized,zeroed}`.
|
||||
|
||||
#![feature(never_type)]
|
||||
#![allow(deprecated, invalid_value)]
|
||||
|
||||
use std::{
|
||||
mem::{self, MaybeUninit, ManuallyDrop},
|
||||
panic,
|
||||
ptr::NonNull,
|
||||
num,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Foo {
|
||||
x: u8,
|
||||
y: !,
|
||||
}
|
||||
|
||||
enum Bar {}
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum OneVariant { Variant(i32) }
|
||||
|
||||
// An enum with ScalarPair layout
|
||||
#[allow(dead_code)]
|
||||
enum LR {
|
||||
Left(i64),
|
||||
Right(i64),
|
||||
}
|
||||
#[allow(dead_code, non_camel_case_types)]
|
||||
enum LR_NonZero {
|
||||
Left(num::NonZeroI64),
|
||||
Right(num::NonZeroI64),
|
||||
}
|
||||
|
||||
fn test_panic_msg<T>(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::<String>()).map(|s| &**s),
|
||||
Some(msg)
|
||||
);
|
||||
}
|
||||
|
||||
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::<Foo>(),
|
||||
"attempted to instantiate uninhabited type `Foo`"
|
||||
);
|
||||
test_panic_msg(
|
||||
|| mem::zeroed::<Foo>(),
|
||||
"attempted to instantiate uninhabited type `Foo`"
|
||||
);
|
||||
test_panic_msg(
|
||||
|| MaybeUninit::<Foo>::uninit().assume_init(),
|
||||
"attempted to instantiate uninhabited type `Foo`"
|
||||
);
|
||||
|
||||
test_panic_msg(
|
||||
|| mem::uninitialized::<Bar>(),
|
||||
"attempted to instantiate uninhabited type `Bar`"
|
||||
);
|
||||
test_panic_msg(
|
||||
|| mem::zeroed::<Bar>(),
|
||||
"attempted to instantiate uninhabited type `Bar`"
|
||||
);
|
||||
test_panic_msg(
|
||||
|| MaybeUninit::<Bar>::uninit().assume_init(),
|
||||
"attempted to instantiate uninhabited type `Bar`"
|
||||
);
|
||||
|
||||
// Types that do not like zero-initialziation
|
||||
test_panic_msg(
|
||||
|| mem::uninitialized::<fn()>(),
|
||||
"attempted to leave type `fn()` uninitialized, which is invalid"
|
||||
);
|
||||
test_panic_msg(
|
||||
|| mem::zeroed::<fn()>(),
|
||||
"attempted to zero-initialize type `fn()`, which is invalid"
|
||||
);
|
||||
|
||||
test_panic_msg(
|
||||
|| mem::uninitialized::<*const dyn Send>(),
|
||||
"attempted to leave type `*const dyn std::marker::Send` uninitialized, which is invalid"
|
||||
);
|
||||
test_panic_msg(
|
||||
|| mem::zeroed::<*const dyn Send>(),
|
||||
"attempted to zero-initialize type `*const dyn std::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(
|
||||
|| mem::uninitialized::<(NonNull<u32>, u32, u32)>(),
|
||||
"attempted to leave type `(std::ptr::NonNull<u32>, u32, u32)` uninitialized, \
|
||||
which is invalid"
|
||||
);
|
||||
test_panic_msg(
|
||||
|| mem::zeroed::<(NonNull<u32>, u32, u32)>(),
|
||||
"attempted to zero-initialize type `(std::ptr::NonNull<u32>, u32, u32)`, \
|
||||
which is invalid"
|
||||
);
|
||||
*/
|
||||
|
||||
// Types that can be zero, but not uninit.
|
||||
test_panic_msg(
|
||||
|| mem::uninitialized::<bool>(),
|
||||
"attempted to leave type `bool` uninitialized, which is invalid"
|
||||
);
|
||||
test_panic_msg(
|
||||
|| mem::uninitialized::<LR>(),
|
||||
"attempted to leave type `LR` uninitialized, which is invalid"
|
||||
);
|
||||
test_panic_msg(
|
||||
|| mem::uninitialized::<ManuallyDrop<LR>>(),
|
||||
"attempted to leave type `std::mem::ManuallyDrop<LR>` uninitialized, which is invalid"
|
||||
);
|
||||
|
||||
// Some things that should work.
|
||||
let _val = mem::zeroed::<bool>();
|
||||
let _val = mem::zeroed::<LR>();
|
||||
let _val = mem::zeroed::<ManuallyDrop<LR>>();
|
||||
let _val = mem::zeroed::<OneVariant>();
|
||||
let _val = mem::zeroed::<Option<&'static i32>>();
|
||||
let _val = mem::zeroed::<MaybeUninit<NonNull<u32>>>();
|
||||
let _val = mem::uninitialized::<MaybeUninit<bool>>();
|
||||
|
||||
// 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
|
||||
// anything about that.
|
||||
let _val = mem::uninitialized::<i32>();
|
||||
let _val = mem::uninitialized::<*const ()>();
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
// run-pass
|
||||
// ignore-wasm32-bare compiled with panic=abort by default
|
||||
// This test checks that instantiating an uninhabited type via `mem::{uninitialized,zeroed}` results
|
||||
// in a runtime panic.
|
||||
|
||||
#![feature(never_type)]
|
||||
#![allow(deprecated, invalid_value)]
|
||||
|
||||
use std::{mem, panic};
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Foo {
|
||||
x: u8,
|
||||
y: !,
|
||||
}
|
||||
|
||||
enum Bar {}
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
panic::catch_unwind(|| {
|
||||
mem::uninitialized::<!>()
|
||||
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
|
||||
s == "Attempted to instantiate uninhabited type !"
|
||||
})),
|
||||
Some(true)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
panic::catch_unwind(|| {
|
||||
mem::zeroed::<!>()
|
||||
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
|
||||
s == "Attempted to instantiate uninhabited type !"
|
||||
})),
|
||||
Some(true)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
panic::catch_unwind(|| {
|
||||
mem::MaybeUninit::<!>::uninit().assume_init()
|
||||
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
|
||||
s == "Attempted to instantiate uninhabited type !"
|
||||
})),
|
||||
Some(true)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
panic::catch_unwind(|| {
|
||||
mem::uninitialized::<Foo>()
|
||||
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
|
||||
s == "Attempted to instantiate uninhabited type Foo"
|
||||
})),
|
||||
Some(true)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
panic::catch_unwind(|| {
|
||||
mem::zeroed::<Foo>()
|
||||
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
|
||||
s == "Attempted to instantiate uninhabited type Foo"
|
||||
})),
|
||||
Some(true)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
panic::catch_unwind(|| {
|
||||
mem::MaybeUninit::<Foo>::uninit().assume_init()
|
||||
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
|
||||
s == "Attempted to instantiate uninhabited type Foo"
|
||||
})),
|
||||
Some(true)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
panic::catch_unwind(|| {
|
||||
mem::uninitialized::<Bar>()
|
||||
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
|
||||
s == "Attempted to instantiate uninhabited type Bar"
|
||||
})),
|
||||
Some(true)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
panic::catch_unwind(|| {
|
||||
mem::zeroed::<Bar>()
|
||||
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
|
||||
s == "Attempted to instantiate uninhabited type Bar"
|
||||
})),
|
||||
Some(true)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
panic::catch_unwind(|| {
|
||||
mem::MaybeUninit::<Bar>::uninit().assume_init()
|
||||
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
|
||||
s == "Attempted to instantiate uninhabited type Bar"
|
||||
})),
|
||||
Some(true)
|
||||
);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user