Skip to content

Commit 2dec553

Browse files
committed
add mem::conjure_zst
1 parent f4665ab commit 2dec553

File tree

3 files changed

+74
-0
lines changed

3 files changed

+74
-0
lines changed

library/core/src/mem/mod.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,3 +1407,55 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) {
14071407
// The `{}` is for better error messages
14081408
{builtin # offset_of($Container, $($fields)+)}
14091409
}
1410+
1411+
/// Create a fresh instance of the inhabited ZST type `T`.
1412+
///
1413+
/// Prefer this to [`zeroed`] or [`uninitialized`] or [`transmute_copy`]
1414+
/// in places where you know that `T` is zero-sized, but don't have a bound
1415+
/// (such as [`Default`]) that would allow you to instantiate it using safe code.
1416+
///
1417+
/// If you're not sure whether `T` is an inhabited ZST, then you should be
1418+
/// using [`MaybeUninit`], not this function.
1419+
///
1420+
/// # Safety
1421+
///
1422+
/// - `size_of::<T>()` must be zero.
1423+
///
1424+
/// - `T` must be *inhabited*. (It must not be a zero-variant `enum`, for example.)
1425+
///
1426+
/// - You must use the value only in ways which do not violate any *safety*
1427+
/// invariants of the type.
1428+
///
1429+
/// While it's easy to create a *valid* instance of an inhabited ZST, since having
1430+
/// no bits in its representation means there's only one possible value, that
1431+
/// doesn't mean that it's always *sound* to do so.
1432+
///
1433+
/// For example, a library with a global semaphore could give out ZST tokens
1434+
/// on `acquire`, and by them being `!Default`+`!Clone` could consume them
1435+
/// in `release` to ensure that it's called at most once per `acquire`.
1436+
/// Or a library could use a `!Default`+`!Send` token to ensure it's used only
1437+
/// from the thread on which it was initialized.
1438+
///
1439+
/// # Examples
1440+
///
1441+
/// ```
1442+
/// #![feature(mem_conjure_zst)]
1443+
/// use std::mem::conjure_zst;
1444+
///
1445+
/// assert_eq!(unsafe { conjure_zst::<()>() }, ());
1446+
/// assert_eq!(unsafe { conjure_zst::<[i32; 0]>() }, []);
1447+
/// ```
1448+
#[unstable(feature = "mem_conjure_zst", issue = "95383")]
1449+
pub const unsafe fn conjure_zst<T>() -> T {
1450+
// This is not a guarantee exposed to clients, but it'll easily optimize out
1451+
// in the sound cases, so we might as well check because we can.
1452+
assert!(size_of::<T>() == 0); // FIXME: use assert_eq! once it's const
1453+
1454+
// SAFETY: because the caller must guarantee that it's inhabited and zero-sized,
1455+
// there's nothing in the representation that needs to be set.
1456+
// `assume_init` calls `assert_inhabited`, so we don't need to here.
1457+
unsafe {
1458+
#[allow(clippy::uninit_assumed_init)]
1459+
MaybeUninit::uninit().assume_init()
1460+
}
1461+
}

tests/ui/consts/std/conjure_zst.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![feature(mem_conjure_zst)]
2+
3+
use std::{convert::Infallible, mem};
4+
5+
const INVALID: Infallible = unsafe { mem::conjure_zst() };
6+
//~^ ERROR attempted to instantiate uninhabited type
7+
8+
const VALID: () = unsafe { mem::conjure_zst() };
9+
10+
fn main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0080]: evaluation panicked: aborted execution: attempted to instantiate uninhabited type `Infallible`
2+
--> $DIR/conjure_zst.rs:5:38
3+
|
4+
LL | const INVALID: Infallible = unsafe { mem::conjure_zst() };
5+
| ^^^^^^^^^^^^^^^^^^ evaluation of `INVALID` failed inside this call
6+
|
7+
note: inside `conjure_zst::<Infallible>`
8+
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)