Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 80ba6dd

Browse files
committedMar 27, 2022
Add mem::conjure_zst for creating ZSTs out of nothing
1 parent 185a3f0 commit 80ba6dd

File tree

6 files changed

+81
-4
lines changed

6 files changed

+81
-4
lines changed
 

‎library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
#![feature(iter_advance_by)]
117117
#![feature(layout_for_ptr)]
118118
#![feature(maybe_uninit_slice)]
119+
#![feature(mem_conjure_zst)]
119120
#![cfg_attr(test, feature(new_uninit))]
120121
#![feature(nonnull_slice_from_raw_parts)]
121122
#![feature(pattern)]

‎library/alloc/src/vec/into_iter.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
147147
self.ptr = unsafe { arith_offset(self.ptr as *const i8, 1) as *mut T };
148148

149149
// Make up a value of this ZST.
150-
Some(unsafe { mem::zeroed() })
150+
Some(unsafe { mem::conjure_zst() })
151151
} else {
152152
let old = self.ptr;
153153
self.ptr = unsafe { self.ptr.offset(1) };
@@ -224,7 +224,7 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
224224
self.end = unsafe { arith_offset(self.end as *const i8, -1) as *mut T };
225225

226226
// Make up a value of this ZST.
227-
Some(unsafe { mem::zeroed() })
227+
Some(unsafe { mem::conjure_zst() })
228228
} else {
229229
self.end = unsafe { self.end.offset(-1) };
230230

‎library/core/src/array/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -799,8 +799,9 @@ where
799799
R: Residual<[T; N]>,
800800
{
801801
if N == 0 {
802-
// SAFETY: An empty array is always inhabited and has no validity invariants.
803-
return unsafe { Some(Try::from_output(mem::zeroed())) };
802+
// SAFETY: An empty array is always inhabited and zero-sized,
803+
// regardless of the size or inhabitedness of its element type.
804+
return unsafe { Some(Try::from_output(mem::conjure_zst())) };
804805
}
805806

806807
struct Guard<'a, T, const N: usize> {

‎library/core/src/mem/mod.rs

+50
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,56 @@ pub unsafe fn uninitialized<T>() -> T {
678678
}
679679
}
680680

681+
/// Create a fresh instance of the inhabited ZST type `T`.
682+
///
683+
/// Prefer this to [`zeroed`] or [`uninitialized`] or [`transmute_copy`]
684+
/// in places where you know that `T` is zero-sized, but don't have a bound
685+
/// (such as [`Default`]) that would allow you to instantiate it using safe code.
686+
///
687+
/// If you're not sure whether `T` is an inhabited ZST, then you should be
688+
/// using [`MaybeUninit`], not this function.
689+
///
690+
/// # Safety
691+
///
692+
/// - `size_of::<T>()` must be zero.
693+
///
694+
/// - `T` must be *inhabited*. (It must not be a zero-variant `enum`, for example.)
695+
///
696+
/// - You must use the value only in ways which do not violate any *safety*
697+
/// invariants of the type.
698+
///
699+
/// While it's easy to create a *valid* instance of an inhabited ZST, since having
700+
/// no bits in its representation means there's only one possible value, that
701+
/// doesn't mean that it's always *sound* to do so.
702+
///
703+
/// For example, a library with a global semaphore could give out ZST tokens
704+
/// on `acquire`, and by them being `!Default`+`!Clone` could consume them
705+
/// in `release` to ensure that it's called at most once per `acquire`.
706+
/// Or a library could use a `!Default`+`!Send` token to ensure it's used only
707+
/// from the thread on which it was initialized.
708+
///
709+
/// # Examples
710+
///
711+
/// ```
712+
/// #![feature(mem_conjure_zst)]
713+
/// use std::mem::conjure_zst;
714+
///
715+
/// assert_eq!(unsafe { conjure_zst::<()>() }, ());
716+
/// assert_eq!(unsafe { conjure_zst::<[i32; 0]>() }, []);
717+
/// ```
718+
#[inline(always)]
719+
#[must_use]
720+
#[unstable(feature = "mem_conjure_zst", issue = "95383")]
721+
#[track_caller]
722+
pub const unsafe fn conjure_zst<T>() -> T {
723+
assert!(size_of::<T>() == 0); // FIXME: Use assert_eq! once that's allowed in const
724+
725+
// SAFETY: because the caller must guarantee that it's inhabited and zero-sized,
726+
// there's nothing in the representation that needs to be set.
727+
// `assume_init` calls `assert_inhabited`, so we don't need to here.
728+
unsafe { MaybeUninit::uninit().assume_init() }
729+
}
730+
681731
/// Swaps the values at two mutable locations, without deinitializing either one.
682732
///
683733
/// * If you want to swap with a default or dummy value, see [`take`].
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(mem_conjure_zst)]
2+
3+
use std::convert::Infallible;
4+
use std::mem::conjure_zst;
5+
6+
// not ok, since the type needs to be inhabited
7+
const CONJURE_INVALID: Infallible = unsafe { conjure_zst() };
8+
//~^ ERROR any use of this value will cause an error
9+
//~^^ WARN will become a hard error in a future release
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: any use of this value will cause an error
2+
--> $DIR/conjure_uninhabited_zst.rs:7:46
3+
|
4+
LL | const CONJURE_INVALID: Infallible = unsafe { conjure_zst() };
5+
| ---------------------------------------------^^^^^^^^^^^^^---
6+
| |
7+
| aborted execution: attempted to instantiate uninhabited type `Infallible`
8+
|
9+
= note: `#[deny(const_err)]` on by default
10+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
11+
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
12+
13+
error: aborting due to previous error
14+

0 commit comments

Comments
 (0)
Failed to load comments.