From b7afe777f79462c9023a30b31785b81e8346c96c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 1 May 2019 19:39:52 +0200 Subject: [PATCH 1/6] stabilize core parts of MaybeUninit and deprecate mem::uninitialized in the future Also expand the documentation a bit --- src/liballoc/lib.rs | 2 +- src/libcore/lib.rs | 2 +- src/libcore/mem.rs | 300 ++++++++---------- src/libstd/lib.rs | 1 - src/test/codegen/box-maybe-uninit.rs | 1 - .../run-pass/panic-uninitialized-zeroed.rs | 2 +- 6 files changed, 138 insertions(+), 170 deletions(-) diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 2edd946ff11cf..d90036eaf49b8 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -109,7 +109,7 @@ #![feature(rustc_const_unstable)] #![feature(const_vec_new)] #![feature(slice_partition_dedup)] -#![feature(maybe_uninit, maybe_uninit_slice, maybe_uninit_array)] +#![feature(maybe_uninit_extra, maybe_uninit_slice, maybe_uninit_array)] #![feature(alloc_layout_extra)] #![feature(try_trait)] #![feature(iter_nth_back)] diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 28db55578c3de..4a70329b64bc9 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -125,7 +125,7 @@ #![feature(structural_match)] #![feature(abi_unadjusted)] #![feature(adx_target_feature)] -#![feature(maybe_uninit, maybe_uninit_slice, maybe_uninit_array)] +#![feature(maybe_uninit_slice, maybe_uninit_array)] #![feature(external_doc)] #[prelude_import] diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 9fb071d29524b..9e23a5e61e4cc 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -465,29 +465,39 @@ pub const fn needs_drop() -> bool { /// Creates a value whose bytes are all zero. /// -/// This has the same effect as allocating space with -/// [`mem::uninitialized`][uninit] and then zeroing it out. It is useful for -/// FFI sometimes, but should generally be avoided. +/// This has the same effect as [`MaybeUninit::zeroed().assume_init()`][zeroed]. +/// It is useful for FFI sometimes, but should generally be avoided. /// /// There is no guarantee that an all-zero byte-pattern represents a valid value of -/// some type `T`. If `T` has a destructor and the value is destroyed (due to -/// a panic or the end of a scope) before being initialized, then the destructor -/// will run on zeroed data, likely leading to [undefined behavior][ub]. +/// some type `T`. For example, the all-zero byte-pattern is not a valid value +/// for reference types (`&T` and `&mut T`). Using `zeroed` on such types +/// causes immediate [undefined behavior][ub]. /// -/// See also the documentation for [`mem::uninitialized`][uninit], which has -/// many of the same caveats. +/// See the documentation of [`MaybeUninit`] and [`MaybeUninit::zeroed()`][zeroed] +/// for more discussion on how to initialize values. /// -/// [uninit]: fn.uninitialized.html +/// [zeroed]: union.MaybeUninit.html#method.zeroed +/// [`MaybeUninit`]: union.MaybeUninit.html /// [ub]: ../../reference/behavior-considered-undefined.html /// /// # Examples /// +/// Correct usage of this function: initializing an integer with zero. +/// /// ``` /// use std::mem; /// /// let x: i32 = unsafe { mem::zeroed() }; /// assert_eq!(0, x); /// ``` +/// +/// *Incorrect* usage of this function: initializing a reference with zero. +/// +/// ```no_run +/// use std::mem; +/// +/// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior! +/// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn zeroed() -> T { @@ -498,130 +508,13 @@ pub unsafe fn zeroed() -> T { /// Bypasses Rust's normal memory-initialization checks by pretending to /// produce a value of type `T`, while doing nothing at all. /// -/// **This is incredibly dangerous and should not be done lightly. Deeply -/// consider initializing your memory with a default value instead.** -/// -/// This is useful for FFI functions and initializing arrays sometimes, -/// but should generally be avoided. -/// -/// # Undefined behavior -/// -/// It is [undefined behavior][ub] to read uninitialized memory, even just an -/// uninitialized boolean. For instance, if you branch on the value of such -/// a boolean, your program may take one, both, or neither of the branches. -/// -/// Writing to the uninitialized value is similarly dangerous. Rust believes the -/// value is initialized, and will therefore try to [`Drop`] the uninitialized -/// value and its fields if you try to overwrite it in a normal manner. The only way -/// to safely initialize an uninitialized value is with [`ptr::write`][write], -/// [`ptr::copy`][copy], or [`ptr::copy_nonoverlapping`][copy_no]. -/// -/// If the value does implement [`Drop`], it must be initialized before -/// it goes out of scope (and therefore would be dropped). Note that this -/// includes a `panic` occurring and unwinding the stack suddenly. -/// -/// If you partially initialize an array, you may need to use -/// [`ptr::drop_in_place`][drop_in_place] to remove the elements you have fully -/// initialized followed by [`mem::forget`][mem_forget] to prevent drop running -/// on the array. If a partially allocated array is dropped this will lead to -/// undefined behaviour. +/// **This functon is deprecated because it basically cannot be used correctly.** /// -/// # Examples -/// -/// Here's how to safely initialize an array of [`Vec`]s. -/// -/// ``` -/// use std::mem; -/// use std::ptr; -/// -/// // Only declare the array. This safely leaves it -/// // uninitialized in a way that Rust will track for us. -/// // However we can't initialize it element-by-element -/// // safely, and we can't use the `[value; 1000]` -/// // constructor because it only works with `Copy` data. -/// let mut data: [Vec; 1000]; -/// -/// unsafe { -/// // So we need to do this to initialize it. -/// data = mem::uninitialized(); -/// -/// // DANGER ZONE: if anything panics or otherwise -/// // incorrectly reads the array here, we will have -/// // Undefined Behavior. -/// -/// // It's ok to mutably iterate the data, since this -/// // doesn't involve reading it at all. -/// // (ptr and len are statically known for arrays) -/// for elem in &mut data[..] { -/// // *elem = Vec::new() would try to drop the -/// // uninitialized memory at `elem` -- bad! -/// // -/// // Vec::new doesn't allocate or do really -/// // anything. It's only safe to call here -/// // because we know it won't panic. -/// ptr::write(elem, Vec::new()); -/// } +/// Use [`MaybeUninit`] instead. /// -/// // SAFE ZONE: everything is initialized. -/// } -/// -/// println!("{:?}", &data[0]); -/// ``` -/// -/// This example emphasizes exactly how delicate and dangerous using `mem::uninitialized` -/// can be. Note that the [`vec!`] macro *does* let you initialize every element with a -/// value that is only [`Clone`], so the following is semantically equivalent and -/// vastly less dangerous, as long as you can live with an extra heap -/// allocation: -/// -/// ``` -/// let data: Vec> = vec![Vec::new(); 1000]; -/// println!("{:?}", &data[0]); -/// ``` -/// -/// This example shows how to handle partially initialized arrays, which could -/// be found in low-level datastructures. -/// -/// ``` -/// use std::mem; -/// use std::ptr; -/// -/// // Count the number of elements we have assigned. -/// let mut data_len: usize = 0; -/// let mut data: [String; 1000]; -/// -/// unsafe { -/// data = mem::uninitialized(); -/// -/// for elem in &mut data[0..500] { -/// ptr::write(elem, String::from("hello")); -/// data_len += 1; -/// } -/// -/// // For each item in the array, drop if we allocated it. -/// for i in &mut data[0..data_len] { -/// ptr::drop_in_place(i); -/// } -/// } -/// // Forget the data. If this is allowed to drop, you may see a crash such as: -/// // 'mem_uninit_test(2457,0x7fffb55dd380) malloc: *** error for object -/// // 0x7ff3b8402920: pointer being freed was not allocated' -/// mem::forget(data); -/// ``` -/// -/// [`Vec`]: ../../std/vec/struct.Vec.html -/// [`vec!`]: ../../std/macro.vec.html -/// [`Clone`]: ../../std/clone/trait.Clone.html -/// [ub]: ../../reference/behavior-considered-undefined.html -/// [write]: ../ptr/fn.write.html -/// [drop_in_place]: ../ptr/fn.drop_in_place.html -/// [mem_zeroed]: fn.zeroed.html -/// [mem_forget]: fn.forget.html -/// [copy]: ../intrinsics/fn.copy.html -/// [copy_no]: ../intrinsics/fn.copy_nonoverlapping.html -/// [`Drop`]: ../ops/trait.Drop.html +/// [`MaybeUninit`]: union.MaybeUninit.html #[inline] -#[rustc_deprecated(since = "2.0.0", reason = "use `mem::MaybeUninit::uninit` instead")] +#[rustc_deprecated(since = "1.40.0", reason = "use `mem::MaybeUninit` instead")] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn uninitialized() -> T { intrinsics::panic_if_uninhabited::(); @@ -899,7 +792,6 @@ pub fn discriminant(v: &T) -> Discriminant { } } -// FIXME: Reference `MaybeUninit` from these docs, once that is stable. /// A wrapper to inhibit compiler from automatically calling `T`’s destructor. /// /// This wrapper is 0-cost. @@ -908,6 +800,7 @@ pub fn discriminant(v: &T) -> Discriminant { /// As a consequence, it has *no effect* on the assumptions that the compiler makes /// about all values being initialized at their type. In particular, initializing /// a `ManuallyDrop<&mut T>` with [`mem::zeroed`] is undefined behavior. +/// If you need to handle uninitialized data, use [`MaybeUninit`] instead. /// /// # Examples /// @@ -942,6 +835,7 @@ pub fn discriminant(v: &T) -> Discriminant { /// ``` /// /// [`mem::zeroed`]: fn.zeroed.html +/// [`MaybeUninit`]: union.MaybeUninit.html #[stable(feature = "manually_drop", since = "1.20.0")] #[lang = "manually_drop"] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -1042,17 +936,18 @@ impl DerefMut for ManuallyDrop { } } -/// A wrapper to construct uninitialized instances of `T`. +/// A wrapper type to construct uninitialized instances of `T`. +/// +/// # Initialization invariant /// /// The compiler, in general, assumes that variables are properly initialized /// at their respective type. For example, a variable of reference type must /// be aligned and non-NULL. This is an invariant that must *always* be upheld, /// even in unsafe code. As a consequence, zero-initializing a variable of reference -/// type causes instantaneous undefined behavior, no matter whether that reference +/// type causes instantaneous [undefined behavior][ub], no matter whether that reference /// ever gets used to access memory: /// /// ```rust,no_run -/// #![feature(maybe_uninit)] /// use std::mem::{self, MaybeUninit}; /// /// let x: &i32 = unsafe { mem::zeroed() }; // undefined behavior! @@ -1067,7 +962,6 @@ impl DerefMut for ManuallyDrop { /// 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! @@ -1078,10 +972,9 @@ impl DerefMut for ManuallyDrop { /// 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: +/// which otherwise can hold any *fixed* bit pattern: /// /// ```rust,no_run -/// #![feature(maybe_uninit)] /// use std::mem::{self, MaybeUninit}; /// /// let x: i32 = unsafe { mem::uninitialized() }; // undefined behavior! @@ -1091,37 +984,108 @@ impl DerefMut for ManuallyDrop { /// (Notice that the rules around uninitialized integers are not finalized yet, but /// until they are, it is advisable to avoid them.) /// +/// On top of that, remember that most types have additional invariants beyond merely +/// being considered initialized at the type level. For example, a `1`-initialized [`Vec`] +/// is considered initialized because the only requirement the compiler knows about it +/// is that the data pointer must be non-null. Creating such a `Vec` does not cause +/// *immediate* undefined behavior, but will cause undefined behavior with most +/// safe operations (including dropping it). +/// +/// [`Vec`]: ../../std/vec/struct.Vec.html +/// +/// # Examples +/// /// `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 -/// #![feature(maybe_uninit)] /// 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: /// let mut x = MaybeUninit::<&i32>::uninit(); /// // Set it to a valid value. -/// x.write(&0); +/// unsafe { x.as_mut_ptr().write(&0); } /// // Extract the initialized data -- this is only allowed *after* properly /// // initializing `x`! /// let x = unsafe { x.assume_init() }; /// ``` /// /// 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. +/// +/// ## Initializing an array element-by-element +/// +/// `MaybeUninit` can be used to initialize a large array element-by-element: +/// +/// ``` +/// use std::mem::{self, MaybeUninit}; +/// use std::ptr; +/// +/// let data = unsafe { +/// // Create an uninitialized array of `MaybeUninit`. The `assume_init` is +/// // safe because the type we are claiming to have initialized here is a +/// // bunch of `MaybeUninit`s, which do not require initialization. +/// let mut data: [MaybeUninit>; 1000] = MaybeUninit::uninit().assume_init(); +/// +/// // Dropping a `MaybeUninit` does nothing, so if there is a panic during this loop, +/// // we have a memory leak, but there is no memory safety issue. +/// for elem in &mut data[..] { +/// ptr::write(elem.as_mut_ptr(), vec![42]); +/// } +/// +/// // Everything is initialized. Transmute the array to the +/// // initialized type. +/// mem::transmute::<_, [Vec; 1000]>(data) +/// }; +/// +/// println!("{:?}", &data[0]); +/// ``` +/// +/// You can also work with partially initialized arrays, which could +/// be found in low-level datastructures. +/// +/// ``` +/// use std::mem::MaybeUninit; +/// use std::ptr; +/// +/// unsafe { +/// // Create an uninitialized array of `MaybeUninit`. The `assume_init` is +/// // safe because the type we are claiming to have initialized here is a +/// // bunch of `MaybeUninit`s, which do not require initialization. +/// let mut data: [MaybeUninit; 1000] = MaybeUninit::uninit().assume_init(); +/// // Count the number of elements we have assigned. +/// let mut data_len: usize = 0; +/// +/// for elem in &mut data[0..500] { +/// ptr::write(elem.as_mut_ptr(), String::from("hello")); +/// data_len += 1; +/// } +/// +/// // For each item in the array, drop if we allocated it. +/// for elem in &mut data[0..data_len] { +/// ptr::drop_in_place(elem.as_mut_ptr()); +/// } +/// } +/// ``` +/// +/// ## Initializing a struct field-by-field +/// +/// There is unfortunately currently no supported way to create a raw pointer or reference +/// to a field of a struct inside `MaybeUninit`. That means it is not possible +/// to create a struct by calling `MaybeUninit::uninit::()` and then writing +/// to its fields. +/// +/// [ub]: ../../reference/behavior-considered-undefined.html #[allow(missing_debug_implementations)] -#[unstable(feature = "maybe_uninit", issue = "53491")] +#[stable(feature = "maybe_uninit", since = "1.36.0")] #[derive(Copy)] -// NOTE: after stabilizing `MaybeUninit`, proceed to deprecate `mem::uninitialized`. pub union MaybeUninit { uninit: (), value: ManuallyDrop, } -#[unstable(feature = "maybe_uninit", issue = "53491")] +#[stable(feature = "maybe_uninit", since = "1.36.0")] impl Clone for MaybeUninit { #[inline(always)] fn clone(&self) -> Self { @@ -1132,10 +1096,13 @@ impl Clone for MaybeUninit { impl MaybeUninit { /// Creates a new `MaybeUninit` initialized with the given value. + /// It is safe to call [`assume_init`] on the return value of this function. /// /// 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")] + /// + /// [`assume_init`]: #method.assume_init + #[stable(feature = "maybe_uninit", since = "1.36.0")] #[inline(always)] pub const fn new(val: T) -> MaybeUninit { MaybeUninit { value: ManuallyDrop::new(val) } @@ -1145,7 +1112,11 @@ impl MaybeUninit { /// /// 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")] + /// + /// See the [type-level documentation][type] for some examples. + /// + /// [type]: union.MaybeUninit.html + #[stable(feature = "maybe_uninit", since = "1.36.0")] #[inline(always)] pub const fn uninit() -> MaybeUninit { MaybeUninit { uninit: () } @@ -1166,7 +1137,6 @@ impl MaybeUninit { /// 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(); @@ -1178,7 +1148,6 @@ impl MaybeUninit { /// cannot hold 0 as a valid value. /// /// ```rust,no_run - /// #![feature(maybe_uninit)] /// use std::mem::MaybeUninit; /// /// enum NotZero { One = 1, Two = 2 }; @@ -1188,7 +1157,7 @@ impl MaybeUninit { /// // Inside a pair, we create a `NotZero` that does not have a valid discriminant. /// // This is undefined behavior. /// ``` - #[unstable(feature = "maybe_uninit", issue = "53491")] + #[stable(feature = "maybe_uninit", since = "1.36.0")] #[inline] pub fn zeroed() -> MaybeUninit { let mut u = MaybeUninit::::uninit(); @@ -1202,7 +1171,7 @@ impl MaybeUninit { /// 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")] + #[unstable(feature = "maybe_uninit_extra", issue = "53491")] #[inline(always)] pub fn write(&mut self, val: T) -> &mut T { unsafe { @@ -1213,13 +1182,14 @@ impl MaybeUninit { /// Gets a pointer to the contained value. Reading from this pointer or turning it /// into a reference is undefined behavior unless the `MaybeUninit` is initialized. + /// Writing to memory that this pointer (non-transitively) points to is undefined behavior + /// (except inside an `UnsafeCell`). /// /// # Examples /// /// Correct usage of this method: /// /// ```rust - /// #![feature(maybe_uninit)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::>::uninit(); @@ -1232,7 +1202,6 @@ impl MaybeUninit { /// *Incorrect* usage of this method: /// /// ```rust,no_run - /// #![feature(maybe_uninit)] /// use std::mem::MaybeUninit; /// /// let x = MaybeUninit::>::uninit(); @@ -1242,7 +1211,7 @@ impl MaybeUninit { /// /// (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")] + #[stable(feature = "maybe_uninit", since = "1.36.0")] #[inline(always)] pub fn as_ptr(&self) -> *const T { unsafe { &*self.value as *const T } @@ -1256,7 +1225,6 @@ impl MaybeUninit { /// Correct usage of this method: /// /// ```rust - /// #![feature(maybe_uninit)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::>::uninit(); @@ -1271,7 +1239,6 @@ impl MaybeUninit { /// *Incorrect* usage of this method: /// /// ```rust,no_run - /// #![feature(maybe_uninit)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::>::uninit(); @@ -1281,7 +1248,7 @@ impl MaybeUninit { /// /// (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")] + #[stable(feature = "maybe_uninit", since = "1.36.0")] #[inline(always)] pub fn as_mut_ptr(&mut self) -> *mut T { unsafe { &mut *self.value as *mut T } @@ -1294,15 +1261,17 @@ impl 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. + /// state. Calling this when the content is not yet fully initialized causes immediate undefined + /// behavior. The [type-level documentation][inv] contains more information about + /// this initialization invariant. + /// + /// [inv]: #initialization-invariant /// /// # Examples /// /// Correct usage of this method: /// /// ```rust - /// #![feature(maybe_uninit)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::::uninit(); @@ -1314,14 +1283,13 @@ impl MaybeUninit { /// *Incorrect* usage of this method: /// /// ```rust,no_run - /// #![feature(maybe_uninit)] /// use std::mem::MaybeUninit; /// /// let x = MaybeUninit::>::uninit(); /// let x_init = unsafe { x.assume_init() }; /// // `x` had not been initialized yet, so this last line caused undefined behavior. /// ``` - #[unstable(feature = "maybe_uninit", issue = "53491")] + #[stable(feature = "maybe_uninit", since = "1.36.0")] #[inline(always)] pub unsafe fn assume_init(self) -> T { intrinsics::panic_if_uninhabited::(); @@ -1338,13 +1306,15 @@ impl MaybeUninit { /// /// 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. + /// behavior. The [type-level documentation][inv] contains more information about + /// this initialization invariant. /// /// Moreover, this leaves a copy of the same data behind in the `MaybeUninit`. When using /// multiple copies of the data (by calling `read` multiple times, or first /// calling `read` and then [`assume_init`]), it is your responsibility /// to ensure that that data may indeed be duplicated. /// + /// [inv]: #initialization-invariant /// [`assume_init`]: #method.assume_init /// /// # Examples @@ -1352,7 +1322,7 @@ impl MaybeUninit { /// Correct usage of this method: /// /// ```rust - /// #![feature(maybe_uninit)] + /// #![feature(maybe_uninit_extra)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::::uninit(); @@ -1373,7 +1343,7 @@ impl MaybeUninit { /// *Incorrect* usage of this method: /// /// ```rust,no_run - /// #![feature(maybe_uninit)] + /// #![feature(maybe_uninit_extra)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::>>::uninit(); @@ -1383,7 +1353,7 @@ impl MaybeUninit { /// // 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")] + #[unstable(feature = "maybe_uninit_extra", issue = "53491")] #[inline(always)] pub unsafe fn read(&self) -> T { intrinsics::panic_if_uninhabited::(); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 2401946536ffa..e044b46e0d076 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -272,7 +272,6 @@ #![feature(libc)] #![feature(link_args)] #![feature(linkage)] -#![feature(maybe_uninit)] #![feature(needs_panic_runtime)] #![feature(never_type)] #![feature(nll)] diff --git a/src/test/codegen/box-maybe-uninit.rs b/src/test/codegen/box-maybe-uninit.rs index 0dd67bb95ccaa..5004f787cde19 100644 --- a/src/test/codegen/box-maybe-uninit.rs +++ b/src/test/codegen/box-maybe-uninit.rs @@ -1,6 +1,5 @@ // compile-flags: -O #![crate_type="lib"] -#![feature(maybe_uninit)] use std::mem::MaybeUninit; diff --git a/src/test/run-pass/panic-uninitialized-zeroed.rs b/src/test/run-pass/panic-uninitialized-zeroed.rs index 3f6e489bb8327..4ca4b407bd4ff 100644 --- a/src/test/run-pass/panic-uninitialized-zeroed.rs +++ b/src/test/run-pass/panic-uninitialized-zeroed.rs @@ -2,7 +2,7 @@ // This test checks that instantiating an uninhabited type via `mem::{uninitialized,zeroed}` results // in a runtime panic. -#![feature(never_type, maybe_uninit)] +#![feature(never_type)] use std::{mem, panic}; From 30a43326d026d4b115bc400094efa0b3e13924ce Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 2 May 2019 16:50:56 +0200 Subject: [PATCH 2/6] apply feedback --- src/libcore/mem.rs | 64 ++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 9e23a5e61e4cc..92e28dc35ba8b 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -471,14 +471,12 @@ pub const fn needs_drop() -> bool { /// There is no guarantee that an all-zero byte-pattern represents a valid value of /// some type `T`. For example, the all-zero byte-pattern is not a valid value /// for reference types (`&T` and `&mut T`). Using `zeroed` on such types -/// causes immediate [undefined behavior][ub]. -/// -/// See the documentation of [`MaybeUninit`] and [`MaybeUninit::zeroed()`][zeroed] -/// for more discussion on how to initialize values. +/// causes immediate [undefined behavior][ub] because [the Rust compiler assumes][inv] +/// that there always is a valid value in a variable it considers initialized. /// /// [zeroed]: union.MaybeUninit.html#method.zeroed -/// [`MaybeUninit`]: union.MaybeUninit.html /// [ub]: ../../reference/behavior-considered-undefined.html +/// [inv]: union.MaybeUninit.html#initialization-invariant /// /// # Examples /// @@ -508,11 +506,21 @@ pub unsafe fn zeroed() -> T { /// Bypasses Rust's normal memory-initialization checks by pretending to /// produce a value of type `T`, while doing nothing at all. /// -/// **This functon is deprecated because it basically cannot be used correctly.** +/// **This functon is deprecated.** Use [`MaybeUninit`] instead. /// -/// Use [`MaybeUninit`] instead. +/// The reason for deprecation is that the function basically cannot be used +/// correctly: [the Rust compiler assumes][inv] that values are properly initialized. +/// As a consequence, calling e.g. `mem::uninitialized::()` causes immediate +/// undefined behavior for returning a `bool` that is not definitely either `true` +/// or `false`. Worse, truly uninitialized memory like what gets returned here +/// 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. +/// (Notice that the rules around uninitialized integers are not finalized yet, but +/// until they are, it is advisable to avoid them.) /// /// [`MaybeUninit`]: union.MaybeUninit.html +/// [inv]: union.MaybeUninit.html#initialization-invariant #[inline] #[rustc_deprecated(since = "1.40.0", reason = "use `mem::MaybeUninit` instead")] #[stable(feature = "rust1", since = "1.0.0")] @@ -1022,21 +1030,23 @@ impl DerefMut for ManuallyDrop { /// use std::mem::{self, MaybeUninit}; /// use std::ptr; /// -/// let data = unsafe { +/// let data = { /// // Create an uninitialized array of `MaybeUninit`. The `assume_init` is /// // safe because the type we are claiming to have initialized here is a /// // bunch of `MaybeUninit`s, which do not require initialization. -/// let mut data: [MaybeUninit>; 1000] = MaybeUninit::uninit().assume_init(); +/// let mut data: [MaybeUninit>; 1000] = unsafe { +/// MaybeUninit::uninit().assume_init() +/// }; /// /// // Dropping a `MaybeUninit` does nothing, so if there is a panic during this loop, /// // we have a memory leak, but there is no memory safety issue. /// for elem in &mut data[..] { -/// ptr::write(elem.as_mut_ptr(), vec![42]); +/// unsafe { ptr::write(elem.as_mut_ptr(), vec![42]); } /// } /// /// // Everything is initialized. Transmute the array to the /// // initialized type. -/// mem::transmute::<_, [Vec; 1000]>(data) +/// unsafe { mem::transmute::<_, [Vec; 1000]>(data) } /// }; /// /// println!("{:?}", &data[0]); @@ -1049,29 +1059,27 @@ impl DerefMut for ManuallyDrop { /// use std::mem::MaybeUninit; /// use std::ptr; /// -/// unsafe { -/// // Create an uninitialized array of `MaybeUninit`. The `assume_init` is -/// // safe because the type we are claiming to have initialized here is a -/// // bunch of `MaybeUninit`s, which do not require initialization. -/// let mut data: [MaybeUninit; 1000] = MaybeUninit::uninit().assume_init(); -/// // Count the number of elements we have assigned. -/// let mut data_len: usize = 0; +/// // Create an uninitialized array of `MaybeUninit`. The `assume_init` is +/// // safe because the type we are claiming to have initialized here is a +/// // bunch of `MaybeUninit`s, which do not require initialization. +/// let mut data: [MaybeUninit; 1000] = unsafe { MaybeUninit::uninit().assume_init() }; +/// // Count the number of elements we have assigned. +/// let mut data_len: usize = 0; /// -/// for elem in &mut data[0..500] { -/// ptr::write(elem.as_mut_ptr(), String::from("hello")); -/// data_len += 1; -/// } +/// for elem in &mut data[0..500] { +/// unsafe { ptr::write(elem.as_mut_ptr(), String::from("hello")); } +/// data_len += 1; +/// } /// -/// // For each item in the array, drop if we allocated it. -/// for elem in &mut data[0..data_len] { -/// ptr::drop_in_place(elem.as_mut_ptr()); -/// } +/// // For each item in the array, drop if we allocated it. +/// for elem in &mut data[0..data_len] { +/// unsafe { ptr::drop_in_place(elem.as_mut_ptr()); } /// } /// ``` /// /// ## Initializing a struct field-by-field /// -/// There is unfortunately currently no supported way to create a raw pointer or reference +/// There is currently no supported way to create a raw pointer or reference /// to a field of a struct inside `MaybeUninit`. That means it is not possible /// to create a struct by calling `MaybeUninit::uninit::()` and then writing /// to its fields. @@ -1183,7 +1191,7 @@ impl MaybeUninit { /// Gets a pointer to the contained value. Reading from this pointer or turning it /// into a reference is undefined behavior unless the `MaybeUninit` is initialized. /// Writing to memory that this pointer (non-transitively) points to is undefined behavior - /// (except inside an `UnsafeCell`). + /// (except inside an `UnsafeCell`). /// /// # Examples /// From 11fba52c0758659b7238d99251e19d3b65c9119b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2019 11:14:58 +0200 Subject: [PATCH 3/6] add out-pointer example --- src/libcore/mem.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 92e28dc35ba8b..cf57945949186 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1022,6 +1022,30 @@ impl DerefMut for ManuallyDrop { /// /// The compiler then knows to not make any incorrect assumptions or optimizations on this code. /// +/// ## out-pointers +/// +/// You can use `MaybeUninit` to implement "out-pointers": instead of returning data +/// from a function, pass it a pointer to some (uninitialized) memory to put the +/// result into. This can be useful when it is important for the caller to control +/// how the memory the result is stored in gets allocated, and you want to avoid +/// unnecessary moves. +/// +/// ``` +/// use std::mem::MaybeUninit; +/// +/// unsafe fn make_vec(out: *mut Vec) { +/// // `write` does not drop the old contents, which is important. +/// out.write(vec![1, 2, 3]); +/// } +/// +/// let mut v: MaybeUninit> = MaybeUninit::uninit(); +/// unsafe { make_vec(v.as_mut_ptr()); } +/// // Now we know `v` is initialized! This also makes sure the vector gets +/// // properly dropped. +/// let v = unsafe { v.assume_init() }; +/// assert_eq!(&v, &[1, 2, 3]); +/// ``` +/// /// ## Initializing an array element-by-element /// /// `MaybeUninit` can be used to initialize a large array element-by-element: @@ -1049,7 +1073,7 @@ impl DerefMut for ManuallyDrop { /// unsafe { mem::transmute::<_, [Vec; 1000]>(data) } /// }; /// -/// println!("{:?}", &data[0]); +/// assert_eq!(&data[0], &[42]); /// ``` /// /// You can also work with partially initialized arrays, which could From 4bf500fea7b9c57d0fb20c9bdade3590b80358f0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 8 May 2019 14:24:14 +0200 Subject: [PATCH 4/6] elliminate mem::uninitialize from docs in libcore --- src/libcore/mem.rs | 56 ---------------------------------------------- src/libcore/ptr.rs | 8 +++---- 2 files changed, 4 insertions(+), 60 deletions(-) diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index cf57945949186..b7af9c0cef9c7 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -63,62 +63,6 @@ pub use crate::intrinsics::transmute; /// The practical use cases for `forget` are rather specialized and mainly come /// up in unsafe or FFI code. /// -/// ## Use case 1 -/// -/// You have created an uninitialized value using [`mem::uninitialized`][uninit]. -/// You must either initialize or `forget` it on every computation path before -/// Rust drops it automatically, like at the end of a scope or after a panic. -/// Running the destructor on an uninitialized value would be [undefined behavior][ub]. -/// -/// ``` -/// use std::mem; -/// use std::ptr; -/// -/// # let some_condition = false; -/// unsafe { -/// let mut uninit_vec: Vec = mem::uninitialized(); -/// -/// if some_condition { -/// // Initialize the variable. -/// ptr::write(&mut uninit_vec, Vec::new()); -/// } else { -/// // Forget the uninitialized value so its destructor doesn't run. -/// mem::forget(uninit_vec); -/// } -/// } -/// ``` -/// -/// ## Use case 2 -/// -/// You have duplicated the bytes making up a value, without doing a proper -/// [`Clone`][clone]. You need the value's destructor to run only once, -/// because a double `free` is undefined behavior. -/// -/// An example is a possible implementation of [`mem::swap`][swap]: -/// -/// ``` -/// use std::mem; -/// use std::ptr; -/// -/// # #[allow(dead_code)] -/// fn swap(x: &mut T, y: &mut T) { -/// unsafe { -/// // Give ourselves some scratch space to work with -/// let mut t: T = mem::uninitialized(); -/// -/// // Perform the swap, `&mut` pointers never alias -/// ptr::copy_nonoverlapping(&*x, &mut t, 1); -/// ptr::copy_nonoverlapping(&*y, x, 1); -/// ptr::copy_nonoverlapping(&t, y, 1); -/// -/// // y and t now point to the same thing, but we need to completely -/// // forget `t` because we do not want to run the destructor for `T` -/// // on its value, which is still owned somewhere outside this function. -/// mem::forget(t); -/// } -/// } -/// ``` -/// /// [drop]: fn.drop.html /// [uninit]: fn.uninitialized.html /// [clone]: ../clone/trait.Clone.html diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index ada784e9ce705..dd321dcdfae6c 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -155,12 +155,12 @@ pub use crate::intrinsics::write_bytes; /// location first: /// ``` /// use std::ptr; -/// use std::mem; +/// use std::mem::{self, MaybeUninit}; /// /// unsafe fn drop_after_copy(to_drop: *mut T) { -/// let mut copy: T = mem::uninitialized(); -/// ptr::copy(to_drop, &mut copy, 1); -/// drop(copy); +/// let mut copy: MaybeUninit = MaybeUninit::uninit(); +/// ptr::copy(to_drop, copy.as_mut_ptr(), 1); +/// drop(copy.assume_init()); /// } /// /// #[repr(packed, C)] From 2a15dec5a2f7f713ffe176a9c1d766114f85aeef Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 6 May 2019 17:39:39 -0400 Subject: [PATCH 5/6] Document layout guarantee of MaybeUninit --- src/libcore/mem.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index b7af9c0cef9c7..24bee6355a7cc 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1053,6 +1053,28 @@ impl DerefMut for ManuallyDrop { /// to its fields. /// /// [ub]: ../../reference/behavior-considered-undefined.html +/// +/// # Layout +/// +/// `MaybeUninit` is guaranteed to have the same size and alignment as `T`: +/// +/// ```rust +/// use std::mem::{MaybeUninit, size_of, align_of}; +/// assert_eq!(size_of::>(), size_of::()); +/// assert_eq!(align_of::>(), align_of::()); +/// ``` +/// +/// However remember that a type *containing* a `MaybeUninit` is not necessarily the same +/// layout; Rust does not in general guarantee that the fields of a `Foo` have the same order as +/// a `Foo` even if `T` and `U` have the same size and alignment. Furthermore because any bit +/// value is valid for a `MaybeUninit` the compiler can't apply non-zero/niche-filling +/// optimizations, potentially resulting in a larger size: +/// +/// ```rust +/// # use std::mem::{MaybeUninit, size_of, align_of}; +/// assert_eq!(size_of::>(), 1); +/// assert_eq!(size_of::>>(), 2); +/// ``` #[allow(missing_debug_implementations)] #[stable(feature = "maybe_uninit", since = "1.36.0")] #[derive(Copy)] From 1916391ea8b97389a9ed690b4df614a2b96069fd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 20 May 2019 10:54:36 +0200 Subject: [PATCH 6/6] update miri --- src/tools/miri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri b/src/tools/miri index 37b2eea7144d0..0c85dbf3df0f5 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit 37b2eea7144d022702f0d2eedcc4c776db15d078 +Subproject commit 0c85dbf3df0f545133dca24eccfc9f0f6107c7f8