Skip to content
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
58 changes: 58 additions & 0 deletions library/core/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

use crate::alloc::Layout;
use crate::marker::DiscriminantKind;
use crate::panic::const_assert;
use crate::{clone, cmp, fmt, hash, intrinsics, ptr};

mod manually_drop;
Expand Down Expand Up @@ -1407,3 +1408,60 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) {
// The `{}` is for better error messages
{builtin # offset_of($Container, $($fields)+)}
}

/// Create a fresh instance of the inhabited ZST type `T`.
///
/// Prefer this to [`zeroed`] or [`uninitialized`] or [`transmute_copy`]
/// in places where you know that `T` is zero-sized, but don't have a bound
/// (such as [`Default`]) that would allow you to instantiate it using safe code.
///
/// If you're not sure whether `T` is an inhabited ZST, then you should be
/// using [`MaybeUninit`], not this function.
///
/// # Panics
///
/// If `size_of::<T>() != 0`.
///
/// # Safety
///
/// - `T` must be *[inhabited]*, i.e. possible to construct. This means that types
/// like zero-variant enums and [`!`] are unsound to conjure.
/// - You must use the value only in ways which do not violate any *safety*
/// invariants of the type.
///
/// While it's easy to create a *valid* instance of an inhabited ZST, since having
/// no bits in its representation means there's only one possible value, that
/// doesn't mean that it's always *sound* to do so.
///
/// For example, a library could design zero-sized tokens that are `!Default + !Clone`, limiting
/// their creation to functions that initialize some state or establish a scope. Conjuring such a
/// token could break invariants and lead to unsoundness.
///
/// # Examples
///
/// ```
/// #![feature(mem_conjure_zst)]
/// use std::mem::conjure_zst;
///
/// assert_eq!(unsafe { conjure_zst::<()>() }, ());
/// assert_eq!(unsafe { conjure_zst::<[i32; 0]>() }, []);
/// ```
///
/// [inhabited]: https://doc.rust-lang.org/reference/glossary.html#inhabited
#[unstable(feature = "mem_conjure_zst", issue = "95383")]
pub const unsafe fn conjure_zst<T>() -> T {
const_assert!(
size_of::<T>() == 0,
"mem::conjure_zst invoked on a nonzero-sized type",
"mem::conjure_zst invoked on type {t}, which is not zero-sized",
t: &str = stringify!(T)
);

// SAFETY: because the caller must guarantee that it's inhabited and zero-sized,
// there's nothing in the representation that needs to be set.
// `assume_init` calls `assert_inhabited`, so we don't need to here.
unsafe {
#[allow(clippy::uninit_assumed_init)]
MaybeUninit::uninit().assume_init()
}
}
10 changes: 10 additions & 0 deletions tests/ui/consts/std/conjure_zst.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(mem_conjure_zst)]

use std::{convert::Infallible, mem};

const INVALID: Infallible = unsafe { mem::conjure_zst() };
//~^ ERROR attempted to instantiate uninhabited type

const VALID: () = unsafe { mem::conjure_zst() };

fn main() {}
12 changes: 12 additions & 0 deletions tests/ui/consts/std/conjure_zst.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0080]: evaluation panicked: aborted execution: attempted to instantiate uninhabited type `Infallible`
--> $DIR/conjure_zst.rs:5:38
|
LL | const INVALID: Infallible = unsafe { mem::conjure_zst() };
| ^^^^^^^^^^^^^^^^^^ evaluation of `INVALID` failed inside this call
|
note: inside `conjure_zst::<Infallible>`
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0080`.
Loading