From 249440ad4cbafdfc535808bc7b7f36e5d3915c97 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 27 Mar 2022 15:09:18 -0700 Subject: [PATCH] Add `mem::conjure_zst` for creating ZSTs out of nothing --- library/core/src/mem/mod.rs | 52 +++++++++++++++++++ .../ui/consts/std/conjure_uninhabited_zst.rs | 11 ++++ .../consts/std/conjure_uninhabited_zst.stderr | 14 +++++ 3 files changed, 77 insertions(+) create mode 100644 tests/ui/consts/std/conjure_uninhabited_zst.rs create mode 100644 tests/ui/consts/std/conjure_uninhabited_zst.stderr diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 78ad6880709..78262511ae8 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -705,6 +705,58 @@ pub unsafe fn uninitialized() -> T { } } +/// 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. +/// +/// # Safety +/// +/// - `size_of::()` must be zero. +/// +/// - `T` must be *inhabited*. (It must not be a zero-variant `enum`, for example.) +/// +/// - 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 with a global semaphore could give out ZST tokens +/// on `acquire`, and by them being `!Default`+`!Clone` could consume them +/// in `release` to ensure that it's called at most once per `acquire`. +/// Or a library could use a `!Default`+`!Send` token to ensure it's used only +/// from the thread on which it was initialized. +/// +/// # Examples +/// +/// ``` +/// #![feature(mem_conjure_zst)] +/// use std::mem::conjure_zst; +/// +/// assert_eq!(unsafe { conjure_zst::<()>() }, ()); +/// assert_eq!(unsafe { conjure_zst::<[i32; 0]>() }, []); +/// ``` +#[inline(always)] +#[must_use] +#[unstable(feature = "mem_conjure_zst", issue = "95383")] +#[track_caller] +pub const unsafe fn conjure_zst() -> T { + // This is not a guarantee exposed to clients, but it'll easily optimize out + // in the sound cases, so we might as well check because we can. + assert!(size_of::() == 0); // FIXME: Use assert_eq! once that's allowed in const + + // 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 { MaybeUninit::uninit().assume_init() } +} + /// Swaps the values at two mutable locations, without deinitializing either one. /// /// * If you want to swap with a default or dummy value, see [`take`]. diff --git a/tests/ui/consts/std/conjure_uninhabited_zst.rs b/tests/ui/consts/std/conjure_uninhabited_zst.rs new file mode 100644 index 00000000000..8eb4c0582e5 --- /dev/null +++ b/tests/ui/consts/std/conjure_uninhabited_zst.rs @@ -0,0 +1,11 @@ +#![feature(mem_conjure_zst)] + +use std::convert::Infallible; +use std::mem::conjure_zst; + +// not ok, since the type needs to be inhabited +const CONJURE_INVALID: Infallible = unsafe { conjure_zst() }; +//~^ ERROR any use of this value will cause an error +//~^^ WARN will become a hard error in a future release + +fn main() {} diff --git a/tests/ui/consts/std/conjure_uninhabited_zst.stderr b/tests/ui/consts/std/conjure_uninhabited_zst.stderr new file mode 100644 index 00000000000..991dc6e3a0d --- /dev/null +++ b/tests/ui/consts/std/conjure_uninhabited_zst.stderr @@ -0,0 +1,14 @@ +error: any use of this value will cause an error + --> $DIR/conjure_uninhabited_zst.rs:7:46 + | +LL | const CONJURE_INVALID: Infallible = unsafe { conjure_zst() }; + | ---------------------------------------------^^^^^^^^^^^^^--- + | | + | aborted execution: attempted to instantiate uninhabited type `Infallible` + | + = 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: aborting due to previous error +