Skip to content

Commit 2663e61

Browse files
authored
Rollup merge of rust-lang#146479 - Qelxiros:mem_conjure_zst, r=scottmcm,tgross35
add mem::conjure_zst Tracking issue: rust-lang#95383
2 parents 5858572 + a9ab29c commit 2663e61

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed

library/core/src/mem/mod.rs

Lines changed: 58 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,60 @@ 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+
/// # Panics
1422+
///
1423+
/// If `size_of::<T>() != 0`.
1424+
///
1425+
/// # Safety
1426+
///
1427+
/// - `T` must be *[inhabited]*, i.e. possible to construct. This means that types
1428+
/// like zero-variant enums and [`!`] are unsound to conjure.
1429+
/// - You must use the value only in ways which do not violate any *safety*
1430+
/// invariants of the type.
1431+
///
1432+
/// While it's easy to create a *valid* instance of an inhabited ZST, since having
1433+
/// no bits in its representation means there's only one possible value, that
1434+
/// doesn't mean that it's always *sound* to do so.
1435+
///
1436+
/// For example, a library could design zero-sized tokens that are `!Default + !Clone`, limiting
1437+
/// their creation to functions that initialize some state or establish a scope. Conjuring such a
1438+
/// token could break invariants and lead to unsoundness.
1439+
///
1440+
/// # Examples
1441+
///
1442+
/// ```
1443+
/// #![feature(mem_conjure_zst)]
1444+
/// use std::mem::conjure_zst;
1445+
///
1446+
/// assert_eq!(unsafe { conjure_zst::<()>() }, ());
1447+
/// assert_eq!(unsafe { conjure_zst::<[i32; 0]>() }, []);
1448+
/// ```
1449+
///
1450+
/// [inhabited]: https://doc.rust-lang.org/reference/glossary.html#inhabited
1451+
#[unstable(feature = "mem_conjure_zst", issue = "95383")]
1452+
pub const unsafe fn conjure_zst<T>() -> T {
1453+
const_assert!(
1454+
size_of::<T>() == 0,
1455+
"mem::conjure_zst invoked on a nonzero-sized type",
1456+
"mem::conjure_zst invoked on type {t}, which is not zero-sized",
1457+
t: &str = stringify!(T)
1458+
);
1459+
1460+
// SAFETY: because the caller must guarantee that it's inhabited and zero-sized,
1461+
// there's nothing in the representation that needs to be set.
1462+
// `assume_init` calls `assert_inhabited`, so we don't need to here.
1463+
unsafe {
1464+
#[allow(clippy::uninit_assumed_init)]
1465+
MaybeUninit::uninit().assume_init()
1466+
}
1467+
}

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)