Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MaybeUninit method array_assume_init #80600

Merged
merged 6 commits into from
Jan 12, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions library/core/src/mem/maybe_uninit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,53 @@ impl<T> MaybeUninit<T> {
}
}

/// Extracts the values from an array of `MaybeUninit` containers.
///
/// # Safety
///
/// It is up to the caller to guarantee that all elements of the array are
/// in an initialized state.
///
/// # Examples
///
/// ```
/// #![feature(maybe_uninit_uninit_array)]
/// #![feature(maybe_uninit_array_assume_init)]
/// use std::mem::MaybeUninit;
///
/// let mut array: [MaybeUninit<i32>; 3] = MaybeUninit::uninit_array();
/// array[0] = MaybeUninit::new(0);
/// array[1] = MaybeUninit::new(1);
/// array[2] = MaybeUninit::new(2);
///
/// // SAFETY: Now safe as we initialised all elements
/// let array = unsafe {
/// MaybeUninit::array_assume_init(array)
/// };
///
/// assert_eq!(array, [0, 1, 2]);
/// ```
#[unstable(feature = "maybe_uninit_array_assume_init", issue = "none")]
#[inline(always)]
pub unsafe fn array_assume_init<const N: usize>(array: [Self; N]) -> [T; N] {
// Convert using a union because mem::transmute does not support const_generics
union ArrayInit<T, const N: usize> {
maybe_uninit: ManuallyDrop<[MaybeUninit<T>; N]>,
init: ManuallyDrop<[T; N]>,
}

// SAFETY:
// * The caller guarantees that all elements of the array are initialized,
// * `MaybeUninit<T>` and T are guaranteed to have the same layout,
// Therefore the conversion is safe
unsafe {
intrinsics::assert_inhabited::<T>();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this assertion really correct?

I tried this code:

#![feature(maybe_uninit_array_assume_init)]

use std::mem::MaybeUninit;

enum Uninhabited {}

fn main() {
    unsafe {
        MaybeUninit::<Uninhabited>::array_assume_init([]);
    }
}
And it panicked with attempted to instantiate uninhabited type `Uninhabited`.
   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 1.12s
     Running `target/debug/playground`
thread 'main' panicked at 'attempted to instantiate uninhabited type `Uninhabited`', /rustc/f4eb5d9f719cd3c849befc8914ad8ce0ddcf34ed/library/core/src/mem/maybe_uninit.rs:842:13
stack backtrace:
   0: rust_begin_unwind
             at /rustc/f4eb5d9f719cd3c849befc8914ad8ce0ddcf34ed/library/std/src/panicking.rs:493:5
   1: core::panicking::panic_fmt
             at /rustc/f4eb5d9f719cd3c849befc8914ad8ce0ddcf34ed/library/core/src/panicking.rs:92:14
   2: core::panicking::panic
             at /rustc/f4eb5d9f719cd3c849befc8914ad8ce0ddcf34ed/library/core/src/panicking.rs:50:5
   3: core::mem::maybe_uninit::MaybeUninit<T>::array_assume_init
             at /rustc/f4eb5d9f719cd3c849befc8914ad8ce0ddcf34ed/library/core/src/mem/maybe_uninit.rs:842:13
   4: playground::main
             at ./src/main.rs:9:9
   5: core::ops::function::FnOnce::call_once
             at /rustc/f4eb5d9f719cd3c849befc8914ad8ce0ddcf34ed/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

But it seems that creating an array of uninhabited type with length of zero is not UB (playground):

#![forbid(unsafe_code)]

#[derive(Debug)]
enum Uninhabited {}

fn main() {
    let not_ub: [Uninhabited; 0] = [];
    dbg!(not_ub);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! This should probably be

if N > 0 { assert_inhabited(); }

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll submit a PR to fix it.


let array = ArrayInit { maybe_uninit: ManuallyDrop::new(array) };
ManuallyDrop::into_inner(array.init)
joboet marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// Assuming all the elements are initialized, get a slice to them.
///
/// # Safety
Expand Down
2 changes: 2 additions & 0 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#![feature(raw)]
#![feature(sort_internals)]
#![feature(slice_partition_at_index)]
#![feature(maybe_uninit_uninit_array)]
#![feature(maybe_uninit_array_assume_init)]
#![feature(maybe_uninit_extra)]
#![feature(maybe_uninit_write_slice)]
#![feature(min_specialization)]
Expand Down
14 changes: 14 additions & 0 deletions library/core/tests/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,20 @@ fn assume_init_good() {
assert!(TRUE);
}

#[test]
fn uninit_array_assume_init() {
let mut array: [MaybeUninit<i16>; 5] = MaybeUninit::uninit_array();
array[0].write(3);
array[1].write(1);
array[2].write(4);
array[3].write(1);
array[4].write(5);

let array = unsafe { MaybeUninit::array_assume_init(array) };

assert_eq!(array, [3, 1, 4, 1, 5]);
}

#[test]
fn uninit_write_slice() {
let mut dst = [MaybeUninit::new(255); 64];
Expand Down