diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs index 20c626cef1b16..edeb65afd67b2 100644 --- a/src/libcore/fmt/float.rs +++ b/src/libcore/fmt/float.rs @@ -15,6 +15,7 @@ fn float_to_decimal_common_exact(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()); @@ -33,6 +34,7 @@ fn float_to_decimal_common_shortest(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()); @@ -71,6 +73,7 @@ fn float_to_exponential_common_exact(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()); @@ -90,6 +93,7 @@ fn float_to_exponential_common_shortest(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()); diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 43afc9a522a30..2b6e7ab9b9950 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1035,7 +1035,7 @@ impl DerefMut for ManuallyDrop { } } -/// A newtype to construct uninitialized instances of `T`. +/// A wrapper to construct uninitialized instances of `T`. /// /// The compiler, in general, assumes that variables are properly initialized /// at their respective type. For example, a variable of reference type must @@ -1049,33 +1049,43 @@ impl DerefMut for ManuallyDrop { /// use std::mem::{self, MaybeUninit}; /// /// let x: &i32 = unsafe { mem::zeroed() }; // undefined behavior! -/// // equivalent code with `MaybeUninit` +/// // The equivalent code with `MaybeUninit<&i32>`: /// let x: &i32 = unsafe { MaybeUninit::zeroed().into_initialized() }; // undefined behavior! /// ``` /// /// This is exploited by the compiler for various optimizations, such as eliding /// run-time checks and optimizing `enum` layout. /// -/// 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! +/// // The equivalent code with `MaybeUninit`: +/// 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 an integer type, +/// which otherwise can hold any bit pattern: /// /// ```rust,no_run /// #![feature(maybe_uninit)] /// use std::mem::{self, MaybeUninit}; /// /// let x: i32 = unsafe { mem::uninitialized() }; // undefined behavior! -/// // equivalent code with `MaybeUninit` +/// // The equivalent code with `MaybeUninit`: /// let x: i32 = unsafe { MaybeUninit::uninitialized().into_initialized() }; // undefined behavior! /// ``` /// (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. +/// It is a signal to the compiler indicating that the data here might *not* /// be initialized: /// /// ```rust @@ -1083,7 +1093,7 @@ impl DerefMut for ManuallyDrop { /// use std::mem::MaybeUninit; /// /// // Create an explicitly uninitialized reference. The compiler knows that data inside -/// // a `MaybeUninit` may be invalid, and hence this is not UB: +/// // a `MaybeUninit` may be invalid, and hence this is not UB: /// let mut x = MaybeUninit::<&i32>::uninitialized(); /// // Set it to a valid value. /// x.set(&0); @@ -1092,20 +1102,30 @@ impl DerefMut for ManuallyDrop { /// 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)] +// NOTE after stabilizing `MaybeUninit` proceed to deprecate `mem::uninitialized`. pub union MaybeUninit { uninit: (), value: ManuallyDrop, } +#[unstable(feature = "maybe_uninit", issue = "53491")] +impl Clone for MaybeUninit { + #[inline(always)] + fn clone(&self) -> Self { + // Not calling T::clone(), we cannot know if we are initialized enough for that. + *self + } +} + impl MaybeUninit { - /// Create a new `MaybeUninit` initialized with the given value. + /// Create a new `MaybeUninit` initialized with the given value. /// - /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. + /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. /// It is your responsibility to make sure `T` gets dropped if it got initialized. #[unstable(feature = "maybe_uninit", issue = "53491")] #[inline(always)] @@ -1113,9 +1133,9 @@ impl MaybeUninit { MaybeUninit { value: ManuallyDrop::new(val) } } - /// Creates a new `MaybeUninit` in an uninitialized state. + /// Creates a new `MaybeUninit` in an uninitialized state. /// - /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. + /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. /// It is your responsibility to make sure `T` gets dropped if it got initialized. #[unstable(feature = "maybe_uninit", issue = "53491")] #[inline(always)] @@ -1123,14 +1143,43 @@ impl MaybeUninit { MaybeUninit { uninit: () } } - /// Creates a new `MaybeUninit` in an uninitialized state, with the memory being + /// Creates a new `MaybeUninit` in an uninitialized state, with the memory being /// filled with `0` bytes. It depends on `T` whether that already makes for /// proper initialization. For example, `MaybeUninit::zeroed()` is initialized, /// but `MaybeUninit<&'static i32>::zeroed()` is not because references must not /// be null. /// - /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. + /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. /// It is your responsibility to make sure `T` gets dropped if it got initialized. + /// + /// # Example + /// + /// Correct usage of this function: initializing a struct with zero, where all + /// fields of the struct can hold the bit-pattern 0 as a valid value. + /// + /// ```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 function: initializing a struct with zero, where some fields + /// 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() }; + /// // Inside a pair, we create a `NotZero` that does not have a valid discriminant. + /// // This is undefined behavior. + /// ``` #[unstable(feature = "maybe_uninit", issue = "53491")] #[inline] pub fn zeroed() -> MaybeUninit { @@ -1141,9 +1190,10 @@ impl MaybeUninit { u } - /// Sets the value of the `MaybeUninit`. This overwrites any previous value without dropping it. - /// For your convenience, this also returns a mutable reference to the (now safely initialized) - /// contents of `self`. + /// Sets the value of the `MaybeUninit`. This overwrites any previous value + /// without dropping it, so be careful not to use this twice unless you want to + /// skip running the destructor. For your convenience, this also returns a mutable + /// reference to the (now safely initialized) contents of `self`. #[unstable(feature = "maybe_uninit", issue = "53491")] #[inline(always)] pub fn set(&mut self, val: T) -> &mut T { @@ -1154,7 +1204,35 @@ impl MaybeUninit { } /// 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. + /// + /// # Examples + /// + /// Correct usage of this method: + /// + /// ```rust + /// #![feature(maybe_uninit)] + /// use std::mem::MaybeUninit; + /// + /// let mut x = MaybeUninit::>::uninitialized(); + /// unsafe { x.as_mut_ptr().write(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::>::uninitialized(); + /// let x_vec = unsafe { &*x.as_ptr() }; + /// // We have created a reference to an uninitialized vector! This is undefined behavior. + /// ``` + /// (Notice that the rules around references to uninitialized data are not finalized yet, but + /// until they are, it is advisable to avoid them.) #[unstable(feature = "maybe_uninit", issue = "53491")] #[inline(always)] pub fn as_ptr(&self) -> *const T { @@ -1162,22 +1240,77 @@ impl MaybeUninit { } /// 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. + /// + /// # Examples + /// + /// Correct usage of this method: + /// + /// ```rust + /// #![feature(maybe_uninit)] + /// use std::mem::MaybeUninit; + /// + /// let mut x = MaybeUninit::>::uninitialized(); + /// unsafe { x.as_mut_ptr().write(vec![0,1,2]); } + /// // Create a reference into the `MaybeUninit>`. + /// // This is okay because we initialized it. + /// 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::>::uninitialized(); + /// let x_vec = unsafe { &mut *x.as_mut_ptr() }; + /// // We have created a reference to an uninitialized vector! This is undefined behavior. + /// ``` + /// (Notice that the rules around references to uninitialized data are not finalized yet, but + /// until they are, it is advisable to avoid them.) #[unstable(feature = "maybe_uninit", issue = "53491")] #[inline(always)] pub fn as_mut_ptr(&mut self) -> *mut T { unsafe { &mut *self.value as *mut T } } - /// Extracts the value from the `MaybeUninit` container. This is a great way + /// Extracts the value from the `MaybeUninit` container. This is a great way /// to ensure that the data will get dropped, because the resulting `T` is /// subject to the usual drop handling. /// /// # Unsafety /// - /// It is up to the caller to guarantee that the `MaybeUninit` really is in an initialized + /// 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::::uninitialized(); + /// unsafe { x.as_mut_ptr().write(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::>::uninitialized(); + /// let x_init = unsafe { x.into_initialized() }; + /// // `x` had not been initialized yet, so this last line caused undefined behavior. + /// ``` #[unstable(feature = "maybe_uninit", issue = "53491")] #[inline(always)] pub unsafe fn into_initialized(self) -> T { @@ -1185,11 +1318,73 @@ impl MaybeUninit { ManuallyDrop::into_inner(self.value) } + /// Reads the value from the `MaybeUninit` container. The resulting `T` is subject + /// to the usual drop handling. + /// + /// Whenever possible, it is preferrable to use [`into_initialized`] instead, which + /// prevents duplicating the content of the `MaybeUninit`. + /// + /// # Safety + /// + /// 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. + /// + /// Moreover, this leaves a copy of the same data behind in the `MaybeUninit`. When using + /// 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. + /// + /// [`into_initialized`]: #method.into_initialized + /// + /// # Examples + /// + /// Correct usage of this method: + /// + /// ```rust + /// #![feature(maybe_uninit)] + /// use std::mem::MaybeUninit; + /// + /// let mut x = MaybeUninit::::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::>>::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::>>::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::(); + self.as_ptr().read() + } + /// Gets a reference to the contained value. /// /// # Unsafety /// - /// It is up to the caller to guarantee that the `MaybeUninit` really is in an initialized + /// 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. #[unstable(feature = "maybe_uninit_ref", issue = "53491")] @@ -1202,7 +1397,7 @@ impl MaybeUninit { /// /// # Unsafety /// - /// It is up to the caller to guarantee that the `MaybeUninit` really is in an initialized + /// 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. // FIXME(#53491): We currently rely on the above being incorrect, i.e., we have references diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 866c8d0896b3c..a2599ae834c69 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -301,7 +301,7 @@ pub unsafe fn swap(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::()` bytes between the two regions of memory