From 39771339fd4f62d4c35676bd7cd1ddb4c5d9b84c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 8 Nov 2018 15:28:06 +0100 Subject: [PATCH 1/5] Allow unsized types in mem::drop and mem::forget --- src/libcore/intrinsics.rs | 4 + src/libcore/lib.rs | 1 + src/libcore/mem.rs | 120 ++++++++++++++++++++++++- src/librustc_codegen_llvm/intrinsic.rs | 2 +- src/librustc_typeck/check/intrinsic.rs | 1 + 5 files changed, 126 insertions(+), 2 deletions(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index cceae9249e456..4fcce7096b4b6 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -714,6 +714,10 @@ extern "rust-intrinsic" { /// initialize memory previous set to the result of `uninit`. pub fn uninit() -> T; + /// Moves a value out of scope without running drop glue. + #[cfg(not(stage0))] + pub fn forget(_: T); + /// Reinterprets the bits of a value of one type as another type. /// /// Both types must have the same size. Neither the original, nor the result, diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 1bbc7892c6bef..b7d0742877ee5 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -107,6 +107,7 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(unboxed_closures)] +#![feature(unsized_locals)] #![feature(untagged_unions)] #![feature(unwind_attributes)] #![feature(doc_alias)] diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 1d0b194487e68..cff605489ed3e 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -139,6 +139,124 @@ pub use intrinsics::transmute; /// [ub]: ../../reference/behavior-considered-undefined.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(stage0))] +pub fn forget(t: T) { + unsafe { intrinsics::forget(t) } +} + +/// Takes ownership and "forgets" about the value **without running its destructor**. +/// +/// Any resources the value manages, such as heap memory or a file handle, will linger +/// forever in an unreachable state. However, it does not guarantee that pointers +/// to this memory will remain valid. +/// +/// * If you want to leak memory, see [`Box::leak`][leak]. +/// * If you want to obtain a raw pointer to the memory, see [`Box::into_raw`][into_raw]. +/// * If you want to dispose of a value properly, running its destructor, see +/// [`mem::drop`][drop]. +/// +/// # Safety +/// +/// `forget` is not marked as `unsafe`, because Rust's safety guarantees +/// do not include a guarantee that destructors will always run. For example, +/// a program can create a reference cycle using [`Rc`][rc], or call +/// [`process::exit`][exit] to exit without running destructors. Thus, allowing +/// `mem::forget` from safe code does not fundamentally change Rust's safety +/// guarantees. +/// +/// That said, leaking resources such as memory or I/O objects is usually undesirable, +/// so `forget` is only recommended for specialized use cases like those shown below. +/// +/// Because forgetting a value is allowed, any `unsafe` code you write must +/// allow for this possibility. You cannot return a value and expect that the +/// caller will necessarily run the value's destructor. +/// +/// [rc]: ../../std/rc/struct.Rc.html +/// [exit]: ../../std/process/fn.exit.html +/// +/// # Examples +/// +/// Leak an I/O object, never closing the file: +/// +/// ```no_run +/// use std::mem; +/// use std::fs::File; +/// +/// let file = File::open("foo.txt").unwrap(); +/// mem::forget(file); +/// ``` +/// +/// 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 +/// [swap]: fn.swap.html +/// [FFI]: ../../book/first-edition/ffi.html +/// [box]: ../../std/boxed/struct.Box.html +/// [leak]: ../../std/boxed/struct.Box.html#method.leak +/// [into_raw]: ../../std/boxed/struct.Box.html#method.into_raw +/// [ub]: ../../reference/behavior-considered-undefined.html +#[inline] +#[cfg(stage0)] +#[stable(feature = "rust1", since = "1.0.0")] pub fn forget(t: T) { ManuallyDrop::new(t); } @@ -763,7 +881,7 @@ pub fn replace(dest: &mut T, mut src: T) -> T { /// [`Copy`]: ../../std/marker/trait.Copy.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] -pub fn drop(_x: T) { } +pub fn drop(_x: T) { } /// Interprets `src` as having type `&U`, and then reads `src` without moving /// the contained value. diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 03244c18ac3e4..1681e0137bd68 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -193,7 +193,7 @@ pub fn codegen_intrinsic_call( return; } // Effectively no-ops - "uninit" => { + "uninit" | "forget" => { return; } "needs_drop" => { diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 3156458b4aa4a..0279105473f2a 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -134,6 +134,7 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, "rustc_peek" => (1, vec![param(0)], param(0)), "init" => (1, Vec::new(), param(0)), "uninit" => (1, Vec::new(), param(0)), + "forget" => (1, vec![param(0)], param(0)), "transmute" => (2, vec![ param(0) ], param(1)), "move_val_init" => { (1, From 06cf9ae7f98f06cde8424ce569efe72c0728c252 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 8 Nov 2018 17:12:14 +0100 Subject: [PATCH 2/5] Fix return type of forget intrinsic --- src/librustc_typeck/check/intrinsic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 0279105473f2a..f34cd53e4c9f3 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -134,7 +134,7 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, "rustc_peek" => (1, vec![param(0)], param(0)), "init" => (1, Vec::new(), param(0)), "uninit" => (1, Vec::new(), param(0)), - "forget" => (1, vec![param(0)], param(0)), + "forget" => (1, vec![param(0)], tcx.mk_unit()), "transmute" => (2, vec![ param(0) ], param(1)), "move_val_init" => { (1, From f211581330399945802a227c4405fd92f983a2b8 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 8 Nov 2018 17:58:12 +0100 Subject: [PATCH 3/5] Use T: ?Sized in intrinsics::forget --- src/libcore/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 4fcce7096b4b6..f795e41f7c5f0 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -716,7 +716,7 @@ extern "rust-intrinsic" { /// Moves a value out of scope without running drop glue. #[cfg(not(stage0))] - pub fn forget(_: T); + pub fn forget(_: T); /// Reinterprets the bits of a value of one type as another type. /// From 4d2934e803d1c948c5e4681a84f33b91c0a0fc64 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 12 Nov 2018 17:19:55 +0100 Subject: [PATCH 4/5] Add forget_unsized only --- src/libcore/mem.rs | 122 ++++++--------------------------------------- 1 file changed, 15 insertions(+), 107 deletions(-) diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index cff605489ed3e..f612f89e0826f 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -139,126 +139,34 @@ pub use intrinsics::transmute; /// [ub]: ../../reference/behavior-considered-undefined.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg(not(stage0))] -pub fn forget(t: T) { - unsafe { intrinsics::forget(t) } +pub fn forget(t: T) { + ManuallyDrop::new(t); } /// Takes ownership and "forgets" about the value **without running its destructor**. /// -/// Any resources the value manages, such as heap memory or a file handle, will linger -/// forever in an unreachable state. However, it does not guarantee that pointers -/// to this memory will remain valid. -/// -/// * If you want to leak memory, see [`Box::leak`][leak]. -/// * If you want to obtain a raw pointer to the memory, see [`Box::into_raw`][into_raw]. -/// * If you want to dispose of a value properly, running its destructor, see -/// [`mem::drop`][drop]. -/// -/// # Safety -/// -/// `forget` is not marked as `unsafe`, because Rust's safety guarantees -/// do not include a guarantee that destructors will always run. For example, -/// a program can create a reference cycle using [`Rc`][rc], or call -/// [`process::exit`][exit] to exit without running destructors. Thus, allowing -/// `mem::forget` from safe code does not fundamentally change Rust's safety -/// guarantees. -/// -/// That said, leaking resources such as memory or I/O objects is usually undesirable, -/// so `forget` is only recommended for specialized use cases like those shown below. -/// -/// Because forgetting a value is allowed, any `unsafe` code you write must -/// allow for this possibility. You cannot return a value and expect that the -/// caller will necessarily run the value's destructor. +/// This function works exactly the same as [`forget`], except it also accepts unsized values. It +/// will never be stabilized and is only available because we haven't decided to relax the bounds +/// on [`forget`] just yet. /// -/// [rc]: ../../std/rc/struct.Rc.html -/// [exit]: ../../std/process/fn.exit.html +/// [`forget`]: fn.forget.html /// /// # Examples /// -/// Leak an I/O object, never closing the file: -/// -/// ```no_run -/// use std::mem; -/// use std::fs::File; -/// -/// let file = File::open("foo.txt").unwrap(); -/// mem::forget(file); -/// ``` -/// -/// 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); -/// } -/// } /// ``` +/// #![feature(forget_unsized)] /// -/// ## 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); -/// } -/// } +/// let f: Box = Box::new(|| ()); +/// let f = *f; +/// mem::forget_unsized(f); /// ``` -/// -/// [drop]: fn.drop.html -/// [uninit]: fn.uninitialized.html -/// [clone]: ../clone/trait.Clone.html -/// [swap]: fn.swap.html -/// [FFI]: ../../book/first-edition/ffi.html -/// [box]: ../../std/boxed/struct.Box.html -/// [leak]: ../../std/boxed/struct.Box.html#method.leak -/// [into_raw]: ../../std/boxed/struct.Box.html#method.into_raw -/// [ub]: ../../reference/behavior-considered-undefined.html #[inline] -#[cfg(stage0)] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn forget(t: T) { - ManuallyDrop::new(t); +#[cfg(not(stage0))] +#[unstable(feature = "forget_unsized", issue = "0")] +pub fn forget_unsized(t: T) { + unsafe { intrinsics::forget(t) } } /// Returns the size of a type in bytes. @@ -881,7 +789,7 @@ pub fn replace(dest: &mut T, mut src: T) -> T { /// [`Copy`]: ../../std/marker/trait.Copy.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] -pub fn drop(_x: T) { } +pub fn drop(_x: T) { } /// Interprets `src` as having type `&U`, and then reads `src` without moving /// the contained value. From 56d3a824e46ce9976a017ef41c318a66444da99c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 12 Nov 2018 18:36:49 +0100 Subject: [PATCH 5/5] Update docs --- src/libcore/mem.rs | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index f612f89e0826f..7fe195d63be7a 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -143,25 +143,12 @@ pub fn forget(t: T) { ManuallyDrop::new(t); } -/// Takes ownership and "forgets" about the value **without running its destructor**. +/// Like [`forget`], but also accepts unsized values. /// -/// This function works exactly the same as [`forget`], except it also accepts unsized values. It -/// will never be stabilized and is only available because we haven't decided to relax the bounds -/// on [`forget`] just yet. +/// This function is just a shim intended to be removed when the `unsized_locals` feature gets +/// stabilized. /// /// [`forget`]: fn.forget.html -/// -/// # Examples -/// -/// ``` -/// #![feature(forget_unsized)] -/// -/// use std::mem; -/// -/// let f: Box = Box::new(|| ()); -/// let f = *f; -/// mem::forget_unsized(f); -/// ``` #[inline] #[cfg(not(stage0))] #[unstable(feature = "forget_unsized", issue = "0")]