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

MaybeUninit: add read_initialized, add examples #58660

Merged
merged 15 commits into from
Mar 9, 2019
Merged
4 changes: 4 additions & 0 deletions src/libcore/fmt/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn float_to_decimal_common_exact<T>(fmt: &mut Formatter, num: &T,
// FIXME(#53491): Technically, this is calling `get_mut` on an uninitialized
// `MaybeUninit` (here and elsewhere in this file). Revisit this once
// we decided whether that is valid or not.
// Using `freeze` is *not enough*; `flt2dec::Part` is an enum!
let formatted = flt2dec::to_exact_fixed_str(flt2dec::strategy::grisu::format_exact,
*num, sign, precision,
false, buf.get_mut(), parts.get_mut());
Expand All @@ -33,6 +34,7 @@ fn float_to_decimal_common_shortest<T>(fmt: &mut Formatter, num: &T,
// enough for f32 and f64
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninitialized();
let mut parts = MaybeUninit::<[flt2dec::Part; 4]>::uninitialized();
// FIXME(#53491)
let formatted = flt2dec::to_shortest_str(flt2dec::strategy::grisu::format_shortest, *num,
sign, precision, false, buf.get_mut(),
parts.get_mut());
Expand Down Expand Up @@ -71,6 +73,7 @@ fn float_to_exponential_common_exact<T>(fmt: &mut Formatter, num: &T,
unsafe {
let mut buf = MaybeUninit::<[u8; 1024]>::uninitialized(); // enough for f32 and f64
let mut parts = MaybeUninit::<[flt2dec::Part; 6]>::uninitialized();
// FIXME(#53491)
let formatted = flt2dec::to_exact_exp_str(flt2dec::strategy::grisu::format_exact,
*num, sign, precision,
upper, buf.get_mut(), parts.get_mut());
Expand All @@ -90,6 +93,7 @@ fn float_to_exponential_common_shortest<T>(fmt: &mut Formatter,
// enough for f32 and f64
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninitialized();
let mut parts = MaybeUninit::<[flt2dec::Part; 6]>::uninitialized();
// FIXME(#53491)
let formatted = flt2dec::to_shortest_exp_str(flt2dec::strategy::grisu::format_shortest,
*num, sign, (0, 0), upper,
buf.get_mut(), parts.get_mut());
Expand Down
208 changes: 196 additions & 12 deletions src/libcore/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1056,12 +1056,22 @@ impl<T: ?Sized> DerefMut for ManuallyDrop<T> {
/// This is exploited by the compiler for various optimizations, such as eliding
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// run-time checks and optimizing `enum` layout.
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
///
/// Not initializing memory at all (instead of zero-initializing it) causes the same
/// issue: after all, the initial value of the variable might just happen to be
/// one that violates the invariant. Moreover, uninitialized memory is special
/// in that the compiler knows that it does not have a fixed value. This makes
/// it undefined behavior to have uninitialized data in a variable even if that
/// variable has otherwise no restrictions about which values are valid:
/// Similarly, entirely uninitialized memory may have any content, while a `bool` must
/// always be `true` or `false`. Hence, creating an uninitialized `bool` is undefined behavior:
///
/// ```rust,no_run
/// #![feature(maybe_uninit)]
/// use std::mem::{self, MaybeUninit};
///
/// let b: bool = unsafe { mem::uninitialized() }; // undefined behavior!
/// // equivalent code with `MaybeUninit`
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// let b: bool = unsafe { MaybeUninit::uninitialized().into_initialized() }; // undefined behavior!
/// ```
///
/// Moreover, uninitialized memory is special in that the compiler knows that
/// it does not have a fixed value. This makes it undefined behavior to have
/// uninitialized data in a variable even if that variable has integer type,
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// which otherwise can hold any bit pattern:
///
/// ```rust,no_run
/// #![feature(maybe_uninit)]
Expand All @@ -1074,8 +1084,8 @@ impl<T: ?Sized> DerefMut for ManuallyDrop<T> {
/// (Notice that the rules around uninitialized integers are not finalized yet, but
/// until they are, it is advisable to avoid them.)
///
/// `MaybeUninit` serves to enable unsafe code to deal with uninitialized data:
/// it is a signal to the compiler indicating that the data here might *not*
/// `MaybeUninit` serves to enable unsafe code to deal with uninitialized data.
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// It is a signal to the compiler indicating that the data here might *not*
/// be initialized:
///
/// ```rust
Expand All @@ -1092,16 +1102,26 @@ impl<T: ?Sized> DerefMut for ManuallyDrop<T> {
/// let x = unsafe { x.into_initialized() };
/// ```
///
/// The compiler then knows to not optimize this code.
/// The compiler then knows to not make any incorrect assumptions or optimizations on this code.
// FIXME before stabilizing, explain how to initialize a struct field-by-field.
#[allow(missing_debug_implementations)]
#[unstable(feature = "maybe_uninit", issue = "53491")]
// NOTE after stabilizing `MaybeUninit` proceed to deprecate `mem::{uninitialized,zeroed}`
#[derive(Copy)]
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you remind me... what was the reason we didn't do this before?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think there was one.

// NOTE after stabilizing `MaybeUninit` proceed to deprecate `mem::uninitialized`
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
pub union MaybeUninit<T> {
uninit: (),
value: ManuallyDrop<T>,
}

#[unstable(feature = "maybe_uninit", issue = "53491")]
impl<T: Copy> Clone for MaybeUninit<T> {
#[inline(always)]
fn clone(&self) -> Self {
// Not calling T::clone(), we cannot know if we are initialized enough for that.
*self
}
}

impl<T> MaybeUninit<T> {
/// Create a new `MaybeUninit` initialized with the given value.
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
///
Expand Down Expand Up @@ -1131,6 +1151,35 @@ impl<T> MaybeUninit<T> {
///
/// Note that dropping a `MaybeUninit` will never call `T`'s drop code.
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// It is your responsibility to make sure `T` gets dropped if it got initialized.
///
/// # Example
///
/// Correct usage of this method: initializing a struct with zero, where all
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// fields of the struct can hold 0 as a valid value.
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
///
/// ```rust
/// #![feature(maybe_uninit)]
/// use std::mem::MaybeUninit;
///
/// let x = MaybeUninit::<(u8, bool)>::zeroed();
/// let x = unsafe { x.into_initialized() };
/// assert_eq!(x, (0, false));
/// ```
///
/// *Incorrect* usage of this method: initializing a struct with zero, where some fields
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// cannot hold 0 as a valid value.
///
/// ```rust,no_run
/// #![feature(maybe_uninit)]
/// use std::mem::MaybeUninit;
///
/// enum NotZero { One = 1, Two = 2 };
///
/// let x = MaybeUninit::<(u8, NotZero)>::zeroed();
/// let x = unsafe { x.into_initialized() };
/// // We create a `NotZero` (inside a pair) that does not have a valid discriminant.
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// // This is undefined behavior.
/// ```
#[unstable(feature = "maybe_uninit", issue = "53491")]
#[inline]
pub fn zeroed() -> MaybeUninit<T> {
Expand All @@ -1154,15 +1203,68 @@ impl<T> MaybeUninit<T> {
}

/// Gets a pointer to the contained value. Reading from this pointer or turning it
/// into a reference will be undefined behavior unless the `MaybeUninit` is initialized.
/// into a reference is undefined behavior unless the `MaybeUninit` is initialized.
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Examples
///
/// Correct usage of this method:
///
/// ```rust
/// #![feature(maybe_uninit)]
/// use std::mem::MaybeUninit;
///
/// let mut x = MaybeUninit::<Vec<u32>>::uninitialized();
/// x.set(vec![0,1,2]);
/// // Create a reference into the `MaybeUninit`. This is okay because we initialized it.
/// let x_vec = unsafe { &*x.as_ptr() };
/// assert_eq!(x_vec.len(), 3);
/// ```
///
/// *Incorrect* usage of this method:
///
/// ```rust,no_run
/// #![feature(maybe_uninit)]
/// use std::mem::MaybeUninit;
///
/// let x = MaybeUninit::<Vec<u32>>::uninitialized();
/// let x_vec = unsafe { &*x.as_ptr() };
/// // We have created a reference to an uninitialized vector! This is undefined behavior.
/// ```
#[unstable(feature = "maybe_uninit", issue = "53491")]
#[inline(always)]
pub fn as_ptr(&self) -> *const T {
unsafe { &*self.value as *const T }
}

/// Gets a mutable pointer to the contained value. Reading from this pointer or turning it
/// into a reference will be undefined behavior unless the `MaybeUninit` is initialized.
/// into a reference is undefined behavior unless the `MaybeUninit` is initialized.
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Examples
///
/// Correct usage of this method:
///
/// ```rust
/// #![feature(maybe_uninit)]
/// use std::mem::MaybeUninit;
///
/// let mut x = MaybeUninit::<Vec<u32>>::uninitialized();
/// x.set(vec![0,1,2]);
/// // Create a reference into the `MaybeUninit`. This is okay because we initialized it.
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// let x_vec = unsafe { &mut *x.as_mut_ptr() };
/// x_vec.push(3);
/// assert_eq!(x_vec.len(), 4);
/// ```
///
/// *Incorrect* usage of this method:
///
/// ```rust,no_run
/// #![feature(maybe_uninit)]
/// use std::mem::MaybeUninit;
///
/// let mut x = MaybeUninit::<Vec<u32>>::uninitialized();
/// let x_vec = unsafe { &mut *x.as_mut_ptr() };
/// // We have created a reference to an uninitialized vector! This is undefined behavior.
/// ```
#[unstable(feature = "maybe_uninit", issue = "53491")]
#[inline(always)]
pub fn as_mut_ptr(&mut self) -> *mut T {
Expand All @@ -1178,13 +1280,95 @@ impl<T> MaybeUninit<T> {
/// It is up to the caller to guarantee that the `MaybeUninit` really is in an initialized
/// state. Calling this when the content is not yet fully initialized causes undefined
/// behavior.
///
/// # Examples
///
/// Correct usage of this method:
///
/// ```rust
/// #![feature(maybe_uninit)]
/// use std::mem::MaybeUninit;
///
/// let mut x = MaybeUninit::<bool>::uninitialized();
/// x.set(true);
/// let x_init = unsafe { x.into_initialized() };
/// assert_eq!(x_init, true);
/// ```
///
/// *Incorrect* usage of this method:
///
/// ```rust,no_run
/// #![feature(maybe_uninit)]
/// use std::mem::MaybeUninit;
///
/// let x = MaybeUninit::<Vec<u32>>::uninitialized();
/// let x_init = unsafe { x.into_initialized() };
/// // `x` had not been initialized yet, so this last line causes undefined behavior.
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// ```
#[unstable(feature = "maybe_uninit", issue = "53491")]
#[inline(always)]
pub unsafe fn into_initialized(self) -> T {
intrinsics::panic_if_uninhabited::<T>();
ManuallyDrop::into_inner(self.value)
}

/// Reads the value from the `MaybeUninit` container. The resulting `T` is subject
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// to the usual drop handling.
///
/// # Unsafety
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
///
/// It is up to the caller to guarantee that the `MaybeUninit` really is in an initialized
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// state. Calling this when the content is not yet fully initialized causes undefined
/// behavior.
///
/// Moreover, this leaves a copy of the same data behind in the `MaybeUninit`. When using
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/// multiple copies of the data (by calling `read_initialized` multiple times, or first
/// calling `read_initialized` and then [`into_initialized`]), it is your responsibility
/// to ensure that that data may indeed be duplicated.
///
/// # Examples
///
/// Correct usage of this method:
///
/// ```rust
/// #![feature(maybe_uninit)]
/// use std::mem::MaybeUninit;
///
/// let mut x = MaybeUninit::<u32>::uninitialized();
/// x.set(13);
/// let x1 = unsafe { x.read_initialized() };
/// // `u32` is `Copy`, so we may read multiple times.
/// let x2 = unsafe { x.read_initialized() };
/// assert_eq!(x1, x2);
///
/// let mut x = MaybeUninit::<Option<Vec<u32>>>::uninitialized();
/// x.set(None);
/// let x1 = unsafe { x.read_initialized() };
/// // Duplicating a `None` value is okay, so we may read multiple times.
/// let x2 = unsafe { x.read_initialized() };
/// assert_eq!(x1, x2);
/// ```
///
/// *Incorrect* usage of this method:
///
/// ```rust,no_run
/// #![feature(maybe_uninit)]
/// use std::mem::MaybeUninit;
///
/// let mut x = MaybeUninit::<Option<Vec<u32>>>::uninitialized();
/// x.set(Some(vec![0,1,2]));
/// let x1 = unsafe { x.read_initialized() };
/// let x2 = unsafe { x.read_initialized() };
/// // We now created two copies of the same vector, leading to a double-free when
/// // they both get dropped!
/// ```
#[unstable(feature = "maybe_uninit", issue = "53491")]
#[inline(always)]
pub unsafe fn read_initialized(&self) -> T {
intrinsics::panic_if_uninhabited::<T>();
self.as_ptr().read()
}

/// Gets a reference to the contained value.
///
/// # Unsafety
Expand Down
2 changes: 1 addition & 1 deletion src/libcore/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ pub unsafe fn swap<T>(x: *mut T, y: *mut T) {
// Perform the swap
copy_nonoverlapping(x, tmp.as_mut_ptr(), 1);
copy(y, x, 1); // `x` and `y` may overlap
copy_nonoverlapping(tmp.get_ref(), y, 1);
copy_nonoverlapping(tmp.as_ptr(), y, 1);
}

/// Swaps `count * size_of::<T>()` bytes between the two regions of memory
Expand Down