Rollup merge of #127446 - zachs18:miri-stdlib-leaks-core-alloc, r=Mark-Simulacrum
Remove memory leaks in doctests in `core`, `alloc`, and `std` cc `@RalfJung` https://github.com/rust-lang/rust/issues/126067 https://github.com/rust-lang/miri/issues/3670 Should be no actual *documentation* changes[^1], all added/modified lines in the doctests are hidden with `#`, This PR splits the existing memory leaks in doctests in `core`, `alloc`, and `std` into two general categories: 1. "Non-focused" memory leaks that are incidental to the thing being documented, and/or are easy to remove, i.e. they are only there because preventing the leak would make the doctest less clear and/or concise. - These doctests simply have a comment like `# // Prevent leaks for Miri.` above the added line that removes the memory leak. - [^2]Some of these would perhaps be better as part of the public documentation part of the doctest, to clarify that a memory leak can happen if it is not otherwise mentioned explicitly in the documentation (specifically the ones in `(A)Rc::increment_strong_count(_in)`). 2. "Focused" memory leaks that are intentional and documented, and/or are possibly fragile to remove. - These doctests have a `# // FIXME` comment above the line that removes the memory leak, with a note that once `-Zmiri-disable-leak-check` can be applied at test granularity, these tests should be "un-unleakified" and have `-Zmiri-disable-leak-check` enabled. - Some of these are possibly fragile (e.g. unleaking the result of `Vec::leak`) and thus should definitely not be made part of the documentation. This should be all of the leaks currently in `core` and `alloc`. I only found one leak in `std`, and it was in the first category (excluding the modules `@RalfJung` mentioned in https://github.com/rust-lang/rust/issues/126067 , and reducing the number of iterations of [one test](https://github.com/rust-lang/rust/blob/master/library/std/src/sync/once_lock.rs#L49-L94) from 1000 to 10) [^1]: assuming [^2] is not added [^2]: backlink
This commit is contained in:
commit
285d45d299
@ -1213,6 +1213,9 @@ pub const fn allocator(b: &Self) -> &A {
|
|||||||
/// let static_ref: &'static mut usize = Box::leak(x);
|
/// let static_ref: &'static mut usize = Box::leak(x);
|
||||||
/// *static_ref += 1;
|
/// *static_ref += 1;
|
||||||
/// assert_eq!(*static_ref, 42);
|
/// assert_eq!(*static_ref, 42);
|
||||||
|
/// # // FIXME(https://github.com/rust-lang/miri/issues/3670):
|
||||||
|
/// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak.
|
||||||
|
/// # drop(unsafe { Box::from_raw(static_ref) });
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Unsized data:
|
/// Unsized data:
|
||||||
@ -1222,6 +1225,9 @@ pub const fn allocator(b: &Self) -> &A {
|
|||||||
/// let static_ref = Box::leak(x);
|
/// let static_ref = Box::leak(x);
|
||||||
/// static_ref[0] = 4;
|
/// static_ref[0] = 4;
|
||||||
/// assert_eq!(*static_ref, [4, 2, 3]);
|
/// assert_eq!(*static_ref, [4, 2, 3]);
|
||||||
|
/// # // FIXME(https://github.com/rust-lang/miri/issues/3670):
|
||||||
|
/// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak.
|
||||||
|
/// # drop(unsafe { Box::from_raw(static_ref) });
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "box_leak", since = "1.26.0")]
|
#[stable(feature = "box_leak", since = "1.26.0")]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1277,6 +1277,8 @@ pub unsafe fn from_raw(ptr: *const T) -> Self {
|
|||||||
///
|
///
|
||||||
/// let five = Rc::from_raw(ptr);
|
/// let five = Rc::from_raw(ptr);
|
||||||
/// assert_eq!(2, Rc::strong_count(&five));
|
/// assert_eq!(2, Rc::strong_count(&five));
|
||||||
|
/// # // Prevent leaks for Miri.
|
||||||
|
/// # Rc::decrement_strong_count(ptr);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -1345,6 +1347,8 @@ pub fn allocator(this: &Self) -> &A {
|
|||||||
/// let x = Rc::new("hello".to_owned());
|
/// let x = Rc::new("hello".to_owned());
|
||||||
/// let x_ptr = Rc::into_raw(x);
|
/// let x_ptr = Rc::into_raw(x);
|
||||||
/// assert_eq!(unsafe { &*x_ptr }, "hello");
|
/// assert_eq!(unsafe { &*x_ptr }, "hello");
|
||||||
|
/// # // Prevent leaks for Miri.
|
||||||
|
/// # drop(unsafe { Rc::from_raw(x_ptr) });
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use = "losing the pointer will leak memory"]
|
#[must_use = "losing the pointer will leak memory"]
|
||||||
#[stable(feature = "rc_raw", since = "1.17.0")]
|
#[stable(feature = "rc_raw", since = "1.17.0")]
|
||||||
@ -1572,6 +1576,8 @@ pub fn strong_count(this: &Self) -> usize {
|
|||||||
///
|
///
|
||||||
/// let five = Rc::from_raw_in(ptr, System);
|
/// let five = Rc::from_raw_in(ptr, System);
|
||||||
/// assert_eq!(2, Rc::strong_count(&five));
|
/// assert_eq!(2, Rc::strong_count(&five));
|
||||||
|
/// # // Prevent leaks for Miri.
|
||||||
|
/// # Rc::decrement_strong_count_in(ptr, System);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1984,6 +1984,9 @@ pub fn into_boxed_str(self) -> Box<str> {
|
|||||||
/// let x = String::from("bucket");
|
/// let x = String::from("bucket");
|
||||||
/// let static_ref: &'static mut str = x.leak();
|
/// let static_ref: &'static mut str = x.leak();
|
||||||
/// assert_eq!(static_ref, "bucket");
|
/// assert_eq!(static_ref, "bucket");
|
||||||
|
/// # // FIXME(https://github.com/rust-lang/miri/issues/3670):
|
||||||
|
/// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak.
|
||||||
|
/// # drop(unsafe { Box::from_raw(static_ref) });
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "string_leak", since = "1.72.0")]
|
#[stable(feature = "string_leak", since = "1.72.0")]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1414,6 +1414,8 @@ pub unsafe fn from_raw(ptr: *const T) -> Self {
|
|||||||
/// // the `Arc` between threads.
|
/// // the `Arc` between threads.
|
||||||
/// let five = Arc::from_raw(ptr);
|
/// let five = Arc::from_raw(ptr);
|
||||||
/// assert_eq!(2, Arc::strong_count(&five));
|
/// assert_eq!(2, Arc::strong_count(&five));
|
||||||
|
/// # // Prevent leaks for Miri.
|
||||||
|
/// # Arc::decrement_strong_count(ptr);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -1484,6 +1486,8 @@ pub fn allocator(this: &Self) -> &A {
|
|||||||
/// let x = Arc::new("hello".to_owned());
|
/// let x = Arc::new("hello".to_owned());
|
||||||
/// let x_ptr = Arc::into_raw(x);
|
/// let x_ptr = Arc::into_raw(x);
|
||||||
/// assert_eq!(unsafe { &*x_ptr }, "hello");
|
/// assert_eq!(unsafe { &*x_ptr }, "hello");
|
||||||
|
/// # // Prevent leaks for Miri.
|
||||||
|
/// # drop(unsafe { Arc::from_raw(x_ptr) });
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use = "losing the pointer will leak memory"]
|
#[must_use = "losing the pointer will leak memory"]
|
||||||
#[stable(feature = "rc_raw", since = "1.17.0")]
|
#[stable(feature = "rc_raw", since = "1.17.0")]
|
||||||
@ -1766,6 +1770,8 @@ pub fn strong_count(this: &Self) -> usize {
|
|||||||
/// // the `Arc` between threads.
|
/// // the `Arc` between threads.
|
||||||
/// let five = Arc::from_raw_in(ptr, System);
|
/// let five = Arc::from_raw_in(ptr, System);
|
||||||
/// assert_eq!(2, Arc::strong_count(&five));
|
/// assert_eq!(2, Arc::strong_count(&five));
|
||||||
|
/// # // Prevent leaks for Miri.
|
||||||
|
/// # Arc::decrement_strong_count_in(ptr, System);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -120,10 +120,15 @@ fn as_raw_mut_slice(&mut self) -> *mut [T] {
|
|||||||
/// This is roughly equivalent to the following, but more efficient
|
/// This is roughly equivalent to the following, but more efficient
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # let mut into_iter = Vec::<u8>::with_capacity(10).into_iter();
|
/// # let mut vec = Vec::<u8>::with_capacity(10);
|
||||||
|
/// # let ptr = vec.as_mut_ptr();
|
||||||
|
/// # let mut into_iter = vec.into_iter();
|
||||||
/// let mut into_iter = std::mem::replace(&mut into_iter, Vec::new().into_iter());
|
/// let mut into_iter = std::mem::replace(&mut into_iter, Vec::new().into_iter());
|
||||||
/// (&mut into_iter).for_each(drop);
|
/// (&mut into_iter).for_each(drop);
|
||||||
/// std::mem::forget(into_iter);
|
/// std::mem::forget(into_iter);
|
||||||
|
/// # // FIXME(https://github.com/rust-lang/miri/issues/3670):
|
||||||
|
/// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak.
|
||||||
|
/// # drop(unsafe { Vec::<u8>::from_raw_parts(ptr, 0, 10) });
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// This method is used by in-place iteration, refer to the vec::in_place_collect
|
/// This method is used by in-place iteration, refer to the vec::in_place_collect
|
||||||
|
@ -1473,6 +1473,9 @@ pub fn allocator(&self) -> &A {
|
|||||||
/// // 2. `0 <= capacity` always holds whatever `capacity` is.
|
/// // 2. `0 <= capacity` always holds whatever `capacity` is.
|
||||||
/// unsafe {
|
/// unsafe {
|
||||||
/// vec.set_len(0);
|
/// vec.set_len(0);
|
||||||
|
/// # // FIXME(https://github.com/rust-lang/miri/issues/3670):
|
||||||
|
/// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak.
|
||||||
|
/// # vec.set_len(3);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@ -2391,6 +2394,9 @@ pub fn resize_with<F>(&mut self, new_len: usize, f: F)
|
|||||||
/// let static_ref: &'static mut [usize] = x.leak();
|
/// let static_ref: &'static mut [usize] = x.leak();
|
||||||
/// static_ref[0] += 1;
|
/// static_ref[0] += 1;
|
||||||
/// assert_eq!(static_ref, &[2, 2, 3]);
|
/// assert_eq!(static_ref, &[2, 2, 3]);
|
||||||
|
/// # // FIXME(https://github.com/rust-lang/miri/issues/3670):
|
||||||
|
/// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak.
|
||||||
|
/// # drop(unsafe { Box::from_raw(static_ref) });
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "vec_leak", since = "1.47.0")]
|
#[stable(feature = "vec_leak", since = "1.47.0")]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -62,6 +62,9 @@ impl<T> ManuallyDrop<T> {
|
|||||||
/// x.truncate(5); // You can still safely operate on the value
|
/// x.truncate(5); // You can still safely operate on the value
|
||||||
/// assert_eq!(*x, "Hello");
|
/// assert_eq!(*x, "Hello");
|
||||||
/// // But `Drop` will not be run here
|
/// // But `Drop` will not be run here
|
||||||
|
/// # // FIXME(https://github.com/rust-lang/miri/issues/3670):
|
||||||
|
/// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak.
|
||||||
|
/// # let _ = ManuallyDrop::into_inner(x);
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use = "if you don't need the wrapper, you can use `mem::forget` instead"]
|
#[must_use = "if you don't need the wrapper, you can use `mem::forget` instead"]
|
||||||
#[stable(feature = "manually_drop", since = "1.20.0")]
|
#[stable(feature = "manually_drop", since = "1.20.0")]
|
||||||
|
@ -274,6 +274,8 @@ impl<T> MaybeUninit<T> {
|
|||||||
/// use std::mem::MaybeUninit;
|
/// use std::mem::MaybeUninit;
|
||||||
///
|
///
|
||||||
/// let v: MaybeUninit<Vec<u8>> = MaybeUninit::new(vec![42]);
|
/// let v: MaybeUninit<Vec<u8>> = MaybeUninit::new(vec![42]);
|
||||||
|
/// # // Prevent leaks for Miri
|
||||||
|
/// # unsafe { let _ = MaybeUninit::assume_init(v); }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`assume_init`]: MaybeUninit::assume_init
|
/// [`assume_init`]: MaybeUninit::assume_init
|
||||||
@ -446,6 +448,9 @@ pub const fn zeroed() -> MaybeUninit<T> {
|
|||||||
/// let mut x = MaybeUninit::<String>::uninit();
|
/// let mut x = MaybeUninit::<String>::uninit();
|
||||||
///
|
///
|
||||||
/// x.write("Hello".to_string());
|
/// x.write("Hello".to_string());
|
||||||
|
/// # // FIXME(https://github.com/rust-lang/miri/issues/3670):
|
||||||
|
/// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak.
|
||||||
|
/// # unsafe { MaybeUninit::assume_init_drop(&mut x); }
|
||||||
/// // This leaks the contained string:
|
/// // This leaks the contained string:
|
||||||
/// x.write("hello".to_string());
|
/// x.write("hello".to_string());
|
||||||
/// // x is initialized now:
|
/// // x is initialized now:
|
||||||
@ -506,6 +511,8 @@ pub const fn write(&mut self, val: T) -> &mut T {
|
|||||||
/// // Create a reference into the `MaybeUninit<T>`. This is okay because we initialized it.
|
/// // Create a reference into the `MaybeUninit<T>`. This is okay because we initialized it.
|
||||||
/// let x_vec = unsafe { &*x.as_ptr() };
|
/// let x_vec = unsafe { &*x.as_ptr() };
|
||||||
/// assert_eq!(x_vec.len(), 3);
|
/// assert_eq!(x_vec.len(), 3);
|
||||||
|
/// # // Prevent leaks for Miri
|
||||||
|
/// # unsafe { MaybeUninit::assume_init_drop(&mut x); }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// *Incorrect* usage of this method:
|
/// *Incorrect* usage of this method:
|
||||||
@ -545,6 +552,8 @@ pub const fn as_ptr(&self) -> *const T {
|
|||||||
/// let x_vec = unsafe { &mut *x.as_mut_ptr() };
|
/// let x_vec = unsafe { &mut *x.as_mut_ptr() };
|
||||||
/// x_vec.push(3);
|
/// x_vec.push(3);
|
||||||
/// assert_eq!(x_vec.len(), 4);
|
/// assert_eq!(x_vec.len(), 4);
|
||||||
|
/// # // Prevent leaks for Miri
|
||||||
|
/// # unsafe { MaybeUninit::assume_init_drop(&mut x); }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// *Incorrect* usage of this method:
|
/// *Incorrect* usage of this method:
|
||||||
@ -746,6 +755,8 @@ pub unsafe fn assume_init_drop(&mut self) {
|
|||||||
/// use std::mem::MaybeUninit;
|
/// use std::mem::MaybeUninit;
|
||||||
///
|
///
|
||||||
/// let mut x = MaybeUninit::<Vec<u32>>::uninit();
|
/// let mut x = MaybeUninit::<Vec<u32>>::uninit();
|
||||||
|
/// # let mut x_mu = x;
|
||||||
|
/// # let mut x = &mut x_mu;
|
||||||
/// // Initialize `x`:
|
/// // Initialize `x`:
|
||||||
/// x.write(vec![1, 2, 3]);
|
/// x.write(vec![1, 2, 3]);
|
||||||
/// // Now that our `MaybeUninit<_>` is known to be initialized, it is okay to
|
/// // Now that our `MaybeUninit<_>` is known to be initialized, it is okay to
|
||||||
@ -755,6 +766,8 @@ pub unsafe fn assume_init_drop(&mut self) {
|
|||||||
/// x.assume_init_ref()
|
/// x.assume_init_ref()
|
||||||
/// };
|
/// };
|
||||||
/// assert_eq!(x, &vec![1, 2, 3]);
|
/// assert_eq!(x, &vec![1, 2, 3]);
|
||||||
|
/// # // Prevent leaks for Miri
|
||||||
|
/// # unsafe { MaybeUninit::assume_init_drop(&mut x_mu); }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ### *Incorrect* usages of this method:
|
/// ### *Incorrect* usages of this method:
|
||||||
@ -1088,6 +1101,8 @@ pub fn copy_from_slice<'a>(this: &'a mut [MaybeUninit<T>], src: &[T]) -> &'a mut
|
|||||||
/// let init = MaybeUninit::clone_from_slice(&mut dst, &src);
|
/// let init = MaybeUninit::clone_from_slice(&mut dst, &src);
|
||||||
///
|
///
|
||||||
/// assert_eq!(init, src);
|
/// assert_eq!(init, src);
|
||||||
|
/// # // Prevent leaks for Miri
|
||||||
|
/// # unsafe { std::ptr::drop_in_place(init); }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -1663,6 +1663,8 @@ pub const fn as_mut_ptr(self) -> *mut T {
|
|||||||
/// // Note that calling `memory.as_mut()` is not allowed here as the content may be uninitialized.
|
/// // Note that calling `memory.as_mut()` is not allowed here as the content may be uninitialized.
|
||||||
/// # #[allow(unused_variables)]
|
/// # #[allow(unused_variables)]
|
||||||
/// let slice: &mut [MaybeUninit<u8>] = unsafe { memory.as_uninit_slice_mut() };
|
/// let slice: &mut [MaybeUninit<u8>] = unsafe { memory.as_uninit_slice_mut() };
|
||||||
|
/// # // Prevent leaks for Miri.
|
||||||
|
/// # unsafe { Global.deallocate(memory.cast(), Layout::new::<[u8; 32]>()); }
|
||||||
/// # Ok::<_, std::alloc::AllocError>(())
|
/// # Ok::<_, std::alloc::AllocError>(())
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -35,6 +35,7 @@ impl WaitTimeoutResult {
|
|||||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||||
/// let pair2 = Arc::clone(&pair);
|
/// let pair2 = Arc::clone(&pair);
|
||||||
///
|
///
|
||||||
|
/// # let handle =
|
||||||
/// thread::spawn(move || {
|
/// thread::spawn(move || {
|
||||||
/// let (lock, cvar) = &*pair2;
|
/// let (lock, cvar) = &*pair2;
|
||||||
///
|
///
|
||||||
@ -58,6 +59,8 @@ impl WaitTimeoutResult {
|
|||||||
/// break
|
/// break
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
/// # // Prevent leaks for Miri.
|
||||||
|
/// # let _ = handle.join();
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||||
|
@ -58,9 +58,8 @@ check-aux:
|
|||||||
library/core \
|
library/core \
|
||||||
library/alloc \
|
library/alloc \
|
||||||
--no-doc
|
--no-doc
|
||||||
# Some doctests have intentional memory leaks.
|
# Some doctests use file system operations to demonstrate dealing with `Result`.
|
||||||
# Some use file system operations to demonstrate dealing with `Result`.
|
$(Q)MIRIFLAGS="-Zmiri-disable-isolation" \
|
||||||
$(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \
|
|
||||||
$(BOOTSTRAP) miri --stage 2 \
|
$(BOOTSTRAP) miri --stage 2 \
|
||||||
library/core \
|
library/core \
|
||||||
library/alloc \
|
library/alloc \
|
||||||
@ -70,7 +69,7 @@ check-aux:
|
|||||||
$(BOOTSTRAP) miri --stage 2 library/std \
|
$(BOOTSTRAP) miri --stage 2 library/std \
|
||||||
--no-doc -- \
|
--no-doc -- \
|
||||||
--skip fs:: --skip net:: --skip process:: --skip sys::pal::
|
--skip fs:: --skip net:: --skip process:: --skip sys::pal::
|
||||||
$(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \
|
$(Q)MIRIFLAGS="-Zmiri-disable-isolation" \
|
||||||
$(BOOTSTRAP) miri --stage 2 library/std \
|
$(BOOTSTRAP) miri --stage 2 library/std \
|
||||||
--doc -- \
|
--doc -- \
|
||||||
--skip fs:: --skip net:: --skip process:: --skip sys::pal::
|
--skip fs:: --skip net:: --skip process:: --skip sys::pal::
|
||||||
|
Loading…
Reference in New Issue
Block a user