Skip to content

Commit 113ca95

Browse files
committed
add mem::conjure_zst
1 parent f4665ab commit 113ca95

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

library/core/src/mem/mod.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use crate::alloc::Layout;
99
use crate::marker::DiscriminantKind;
10+
use crate::panic::const_assert;
1011
use crate::{clone, cmp, fmt, hash, intrinsics, ptr};
1112

1213
mod manually_drop;
@@ -1407,3 +1408,59 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) {
14071408
// The `{}` is for better error messages
14081409
{builtin # offset_of($Container, $($fields)+)}
14091410
}
1411+
1412+
/// Create a fresh instance of the inhabited ZST type `T`.
1413+
///
1414+
/// Prefer this to [`zeroed`] or [`uninitialized`] or [`transmute_copy`]
1415+
/// in places where you know that `T` is zero-sized, but don't have a bound
1416+
/// (such as [`Default`]) that would allow you to instantiate it using safe code.
1417+
///
1418+
/// If you're not sure whether `T` is an inhabited ZST, then you should be
1419+
/// using [`MaybeUninit`], not this function.
1420+
///
1421+
/// # Safety
1422+
///
1423+
/// - `size_of::<T>()` must be zero.
1424+
/// - `T` must be *[inhabited]*, i.e. possible to construct. This means that types
1425+
/// like zero-variant enums and [`!`] are unsound to conjure.
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 could design zero-sized tokens that are `!Default + !Clone`, limiting
1434+
/// their creation to functions that initialize some state or establish a scope. Conjuring such a
1435+
/// token could break invariants and lead to unsoundness.
1436+
///
1437+
/// # Examples
1438+
///
1439+
/// ```
1440+
/// #![feature(mem_conjure_zst)]
1441+
/// use std::mem::conjure_zst;
1442+
///
1443+
/// assert_eq!(unsafe { conjure_zst::<()>() }, ());
1444+
/// assert_eq!(unsafe { conjure_zst::<[i32; 0]>() }, []);
1445+
/// ```
1446+
///
1447+
/// [inhabited]: https://doc.rust-lang.org/reference/glossary.html#inhabited
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+
const_assert!(
1453+
size_of::<T>() == 0,
1454+
"mem::conjure_zst invoked on a nonzero-sized type",
1455+
"mem::conjure_zst invoked on type {t}, which is not zero-sized",
1456+
t: &str = stringify!(T)
1457+
);
1458+
1459+
// SAFETY: because the caller must guarantee that it's inhabited and zero-sized,
1460+
// there's nothing in the representation that needs to be set.
1461+
// `assume_init` calls `assert_inhabited`, so we don't need to here.
1462+
unsafe {
1463+
#[allow(clippy::uninit_assumed_init)]
1464+
MaybeUninit::uninit().assume_init()
1465+
}
1466+
}

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)