rust-lang/rust · #161702
Use `drop_guard` in some places in {core,alloc,std}
library/alloc/src/boxed/thin.rs13 + / 27 −
@@ -11,7 +11,7 @@ use core::marker::PhantomData; use core::marker::Unsize; #[cfg(not(no_global_oom_handling))] use core::mem;-use core::mem::SizedTypeProperties;+use core::mem::{DropGuard, SizedTypeProperties}; use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull, Pointee}; @@ -364,38 +364,24 @@ impl<H> WithHeader<H> { // - Assumes that either `value` can be dereferenced, or is the // `NonNull::dangling()` we use when both `T` and `H` are ZSTs. unsafe fn drop<T: ?Sized>(&self, value: *mut T) {- struct DropGuard<H> {- ptr: NonNull<u8>,- value_layout: Layout,- _marker: PhantomData<H>,- }-- impl<H> Drop for DropGuard<H> {- fn drop(&mut self) {- // All ZST are allocated statically.- if self.value_layout.size() == 0 {- return;- }+ // SAFETY: Caller ensures `value` is valid.+ let value_layout = unsafe { Layout::for_value_raw(value) }; - let (layout, value_offset) =- // SAFETY: Layout must have been computable if we're in drop- unsafe { WithHeader::<H>::alloc_layout(self.value_layout).unwrap_unchecked() };+ let _guard; + // All ZST are allocated statically.+ if value_layout.size() != 0 {+ _guard = DropGuard::new(self.0, |ptr| {+ let layout = WithHeader::<H>::alloc_layout(value_layout);+ // SAFETY: Layout must have been computable if we're in this callback+ let (layout, value_offset) = unsafe { layout.unwrap_unchecked() }; // Since we only allocate for non-ZSTs, the layout size cannot be zero.- debug_assert!(layout.size() != 0);+ debug_assert_ne!(layout.size(), 0); // SAFETY: We own the allocation with `layout` at `ptr - value_offset`.- unsafe { alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout) };- }+ unsafe { alloc::dealloc(ptr.as_ptr().sub(value_offset), layout) };+ }); } - // `_guard` will deallocate the memory when dropped, even if `drop_in_place` unwinds.- let _guard = DropGuard {- ptr: self.0,- // SAFETY: Caller ensures `value` is valid.- value_layout: unsafe { Layout::for_value_raw(value) },- _marker: PhantomData::<H>,- };- // We only drop the value because the Pointee trait requires that the metadata is copy // aka trivially droppable. // SAFETY: We're the only droppers of `value` and it's not dropped again.library/alloc/src/collections/binary_heap/mod.rs3 + / 11 −
@@ -145,7 +145,7 @@ use core::alloc::Allocator; use core::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedFused, TrustedLen};-use core::mem::{self, ManuallyDrop, swap};+use core::mem::{DropGuard, ManuallyDrop, swap}; use core::num::NonZero; use core::ops::{Deref, DerefMut}; use core::{fmt, ptr};@@ -1914,18 +1914,10 @@ impl<'a, T: Ord, A: Allocator> DrainSorted<'a, T, A> { impl<'a, T: Ord, A: Allocator> Drop for DrainSorted<'a, T, A> { /// Removes heap elements in heap order. fn drop(&mut self) {- struct DropGuard<'r, 'a, T: Ord, A: Allocator>(&'r mut DrainSorted<'a, T, A>);-- impl<'r, 'a, T: Ord, A: Allocator> Drop for DropGuard<'r, 'a, T, A> {- fn drop(&mut self) {- while self.0.inner.pop().is_some() {}- }- }- while let Some(item) = self.inner.pop() {- let guard = DropGuard(self);+ let guard = DropGuard::new(&mut *self, |this| while this.inner.pop().is_some() {}); drop(item);- mem::forget(guard);+ DropGuard::dismiss(guard); } } }library/alloc/src/collections/btree/map.rs6 + / 12 −
@@ -5,7 +5,7 @@ use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; use core::iter::{FusedIterator, TrustedLen}; use core::marker::PhantomData;-use core::mem::{self, ManuallyDrop};+use core::mem::{self, DropGuard, ManuallyDrop}; use core::ops::{Bound, Index, RangeBounds}; use core::ptr; @@ -1912,24 +1912,18 @@ impl<K, V, A: AllocatorClone> IntoIterator for BTreeMap<K, V, A> { #[stable(feature = "btree_drop", since = "1.7.0")] impl<K, V, A: AllocatorClone> Drop for IntoIter<K, V, A> { fn drop(&mut self) {- struct DropGuard<'a, K, V, A: AllocatorClone>(&'a mut IntoIter<K, V, A>);-- impl<'a, K, V, A: AllocatorClone> Drop for DropGuard<'a, K, V, A> {- fn drop(&mut self) {+ while let Some(kv) = self.dying_next() {+ let guard = DropGuard::new(&mut *self, |this| { // Continue the same loop we perform below. This only runs when unwinding, so we // don't have to care about panics this time (they'll abort).- while let Some(kv) = self.0.dying_next() {+ while let Some(kv) = this.dying_next() { // SAFETY: we consume the dying handle immediately. unsafe { kv.drop_key_val() }; }- }- }-- while let Some(kv) = self.dying_next() {- let guard = DropGuard(self);+ }); // SAFETY: we don't touch the tree before consuming the dying handle. unsafe { kv.drop_key_val() };- mem::forget(guard);+ DropGuard::dismiss(guard); } } }library/alloc/src/collections/btree/mem.rs2 + / 8 −
@@ -16,20 +16,14 @@ pub(super) fn take_mut<T>(v: &mut T, change: impl FnOnce(T) -> T) { /// If a panic occurs in the `change` closure, the entire process will be aborted. #[inline] pub(super) fn replace<T, R>(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R {- struct PanicGuard;- impl Drop for PanicGuard {- fn drop(&mut self) {- intrinsics::abort()- }- }- let guard = PanicGuard;+ let guard = mem::DropGuard::new((), |()| intrinsics::abort()); // SAFETY: v is valid for reads and we write a new value before returning. let value = unsafe { ptr::read(v) }; let (new_value, ret) = change(value); // SAFETY: new_value is T and v is valid for writes. unsafe { ptr::write(v, new_value); }- mem::forget(guard);+ mem::DropGuard::dismiss(guard); ret }library/alloc/src/collections/btree/node.rs3 + / 14 −
@@ -32,7 +32,7 @@ // an edge both identifies a position and contains a pointer to a child node. use core::marker::PhantomData;-use core::mem::{self, MaybeUninit};+use core::mem::{self, DropGuard, MaybeUninit}; use core::num::NonZero; use core::ptr::{self, NonNull}; use core::slice::SliceIndex;@@ -1237,25 +1237,14 @@ impl<K, V, NodeType> Handle<NodeRef<marker::Dying, K, V, NodeType>, marker::KV> /// The node that the handle refers to must not yet have been deallocated. #[inline] pub(super) unsafe fn drop_key_val(mut self) {- // Run the destructor of the value even if the destructor of the key panics.- struct Dropper<'a, T>(&'a mut MaybeUninit<T>);- impl<T> Drop for Dropper<'_, T> {- #[inline]- fn drop(&mut self) {- // ignore-tidy-undocumented-unsafe- unsafe {- self.0.assume_init_drop();- }- }- }- debug_assert!(self.idx < self.node.len()); let leaf = self.node.as_leaf_dying(); // ignore-tidy-undocumented-unsafe unsafe { let key = leaf.keys.get_unchecked_mut(self.idx); let val = leaf.vals.get_unchecked_mut(self.idx);- let _guard = Dropper(val);+ // Run the destructor of the value even if the destructor of the key panics.+ let _guard = DropGuard::new(val, |val| val.assume_init_drop()); key.assume_init_drop(); // dropping the guard will drop the value }library/alloc/src/collections/linked_list.rs9 + / 13 −
@@ -17,6 +17,7 @@ use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::iter::{FusedIterator, TrustedLen}; use core::marker::PhantomData;+use core::mem::DropGuard; use core::ptr::NonNull; use core::{fmt, mem}; @@ -1192,20 +1193,15 @@ impl<T, A: Allocator> LinkedList<T, A> { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T, A: Allocator> Drop for LinkedList<T, A> { fn drop(&mut self) {- struct DropGuard<'a, T, A: Allocator>(&'a mut LinkedList<T, A>);-- impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> {- fn drop(&mut self) {- // Continue the same loop we do below. This only runs when a destructor has- // panicked. If another one panics this will abort.- while self.0.pop_front_node().is_some() {}- }- }- // Wrap self so that if a destructor panics, we can try to keep looping- let guard = DropGuard(self);- while guard.0.pop_front_node().is_some() {}- mem::forget(guard);+ let mut guard = DropGuard::new(self, |this| {+ // Continue the same loop we do below. This only runs when a destructor has+ // panicked. If another one panics this will abort.+ while this.pop_front_node().is_some() {}+ });++ while guard.pop_front_node().is_some() {}+ DropGuard::dismiss(guard); } } library/alloc/src/collections/vec_deque/drain.rs120 + / 127 −
@@ -1,6 +1,6 @@ use core::iter::FusedIterator; use core::marker::PhantomData;-use core::mem::{self, SizedTypeProperties};+use core::mem::{self, DropGuard, SizedTypeProperties}; use core::ptr::NonNull; use core::{fmt, ptr}; @@ -94,144 +94,137 @@ unsafe impl<T: Send, A: Allocator + Send> Send for Drain<'_, T, A> {} #[stable(feature = "drain", since = "1.6.0")] impl<T, A: Allocator> Drop for Drain<'_, T, A> { fn drop(&mut self) {- struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>);-- let guard = DropGuard(self);-- if mem::needs_drop::<T>() && guard.0.remaining != 0 {- // SAFETY: We just checked that `self.remaining != 0`.- let (front, back) = unsafe { guard.0.as_slices() };- // since idx is a logical index, we don't need to worry about wrapping.- guard.0.idx += front.len();- guard.0.remaining -= front.len();- // SAFETY: This can't have been dropped before since- // `idx` & `remaining` track what's been dropped.- unsafe { ptr::drop_in_place(front) };- guard.0.remaining = 0;- // SAFETY: Ditto.- unsafe { ptr::drop_in_place(back) };- }- // Dropping `guard` handles moving the remaining elements into place.- impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> {- #[inline]- fn drop(&mut self) {- if mem::needs_drop::<T>() && self.0.remaining != 0 {- // SAFETY: We just checked that `self.remaining != 0`.- unsafe {- let (front, back) = self.0.as_slices();- ptr::drop_in_place(front);- ptr::drop_in_place(back);- }+ let mut guard = DropGuard::new(self, |drain| {+ if mem::needs_drop::<T>() && drain.remaining != 0 {+ // SAFETY: We just checked that `self.remaining != 0`.+ unsafe {+ let (front, back) = drain.as_slices();+ ptr::drop_in_place(front);+ ptr::drop_in_place(back); }+ } - // ignore-tidy-undocumented-unsafe- let source_deque = unsafe { self.0.deque.as_mut() };+ // ignore-tidy-undocumented-unsafe+ let source_deque = unsafe { drain.deque.as_mut() }; - let drain_len = self.0.drain_len;- let head_len = source_deque.len; // #elements in front of the drain- let tail_len = self.0.tail_len; // #elements behind the drain- let new_len = head_len + tail_len;+ let drain_len = drain.drain_len;+ let head_len = source_deque.len; // #elements in front of the drain+ let tail_len = drain.tail_len; // #elements behind the drain+ let new_len = head_len + tail_len; - if T::IS_ZST {- // no need to copy around any memory if T is a ZST- source_deque.len = new_len;- return;- }+ if T::IS_ZST {+ // no need to copy around any memory if T is a ZST+ source_deque.len = new_len;+ return;+ } - // Next, we will fill the hole left by the drain with as few writes as possible.- // The code below handles the following control flow and reduces the amount of- // branches under the assumption that `head_len == 0 || tail_len == 0`, i.e.- // draining at the front or at the back of the dequeue is especially common.- //- // H = "head index" = `deque.head`- // h = elements in front of the drain- // d = elements in the drain- // t = elements behind the drain- //- // Note that the buffer may wrap at any point and the wrapping is handled by- // `wrap_copy` and `to_physical_idx`.- //- // Case 1: if `head_len == 0 && tail_len == 0`- // Everything was drained, reset the head index back to 0.- // H- // [ . . . . . d d d d . . . . . ]- // H- // [ . . . . . . . . . . . . . . ]- //- // Case 2: else if `tail_len == 0`- // Don't move data or the head index.- // H- // [ . . . h h h h d d d d . . . ]- // H- // [ . . . h h h h . . . . . . . ]- //- // Case 3: else if `head_len == 0`- // Don't move data, but move the head index.- // H- // [ . . . d d d d t t t t . . . ]- // H- // [ . . . . . . . t t t t . . . ]- //- // Case 4: else if `tail_len <= head_len`- // Move data, but not the head index.- // H- // [ . . h h h h d d d d t t . . ]- // H- // [ . . h h h h t t . . . . . . ]- //- // Case 5: else- // Move data and the head index.- // H- // [ . . h h d d d d t t t t . . ]- // H- // [ . . . . . . h h t t t t . . ]+ // Next, we will fill the hole left by the drain with as few writes as possible.+ // The code below handles the following control flow and reduces the amount of+ // branches under the assumption that `head_len == 0 || tail_len == 0`, i.e.+ // draining at the front or at the back of the dequeue is especially common.+ //+ // H = "head index" = `deque.head`+ // h = elements in front of the drain+ // d = elements in the drain+ // t = elements behind the drain+ //+ // Note that the buffer may wrap at any point and the wrapping is handled by+ // `wrap_copy` and `to_physical_idx`.+ //+ // Case 1: if `head_len == 0 && tail_len == 0`+ // Everything was drained, reset the head index back to 0.+ // H+ // [ . . . . . d d d d . . . . . ]+ // H+ // [ . . . . . . . . . . . . . . ]+ //+ // Case 2: else if `tail_len == 0`+ // Don't move data or the head index.+ // H+ // [ . . . h h h h d d d d . . . ]+ // H+ // [ . . . h h h h . . . . . . . ]+ //+ // Case 3: else if `head_len == 0`+ // Don't move data, but move the head index.+ // H+ // [ . . . d d d d t t t t . . . ]+ // H+ // [ . . . . . . . t t t t . . . ]+ //+ // Case 4: else if `tail_len <= head_len`+ // Move data, but not the head index.+ // H+ // [ . . h h h h d d d d t t . . ]+ // H+ // [ . . h h h h t t . . . . . . ]+ //+ // Case 5: else+ // Move data and the head index.+ // H+ // [ . . h h d d d d t t t t . . ]+ // H+ // [ . . . . . . h h t t t t . . ] - // When draining at the front (`.drain(..n)`) or at the back (`.drain(n..)`),- // we don't need to copy any data. The number of elements copied would be 0.- if head_len != 0 && tail_len != 0 {- join_head_and_tail_wrapping(source_deque, drain_len, head_len, tail_len);- // Marking this function as cold helps LLVM to eliminate it entirely if- // this branch is never taken.- // We use `#[cold]` instead of `#[inline(never)]`, because inlining this- // function into the general case (`.drain(n..m)`) is fine.- // See `tests/codegen-llvm/vecdeque-drain.rs` for a test.- #[cold]- fn join_head_and_tail_wrapping<T, A: Allocator>(- source_deque: &mut VecDeque<T, A>,- drain_len: usize,- head_len: usize,- tail_len: usize,- ) {- // Pick whether to move the head or the tail here.- let (src, dst, len);- if head_len < tail_len {- src = source_deque.head;- dst = source_deque.to_wrapped_index(drain_len);- len = head_len;- } else {- src = source_deque.to_wrapped_index(head_len + drain_len);- dst = source_deque.to_wrapped_index(head_len);- len = tail_len;- };+ // When draining at the front (`.drain(..n)`) or at the back (`.drain(n..)`),+ // we don't need to copy any data. The number of elements copied would be 0.+ if head_len != 0 && tail_len != 0 {+ join_head_and_tail_wrapping(source_deque, drain_len, head_len, tail_len);+ // Marking this function as cold helps LLVM to eliminate it entirely if+ // this branch is never taken.+ // We use `#[cold]` instead of `#[inline(never)]`, because inlining this+ // function into the general case (`.drain(n..m)`) is fine.+ // See `tests/codegen-llvm/vecdeque-drain.rs` for a test.+ #[cold]+ fn join_head_and_tail_wrapping<T, A: Allocator>(+ source_deque: &mut VecDeque<T, A>,+ drain_len: usize,+ head_len: usize,+ tail_len: usize,+ ) {+ // Pick whether to move the head or the tail here.+ let (src, dst, len);+ if head_len < tail_len {+ src = source_deque.head;+ dst = source_deque.to_wrapped_index(drain_len);+ len = head_len;+ } else {+ src = source_deque.to_wrapped_index(head_len + drain_len);+ dst = source_deque.to_wrapped_index(head_len);+ len = tail_len;+ }; - // ignore-tidy-undocumented-unsafe- unsafe {- source_deque.wrap_copy(src, dst, len);- }+ // ignore-tidy-undocumented-unsafe+ unsafe {+ source_deque.wrap_copy(src, dst, len); } }+ } - if new_len == 0 {- // Special case: If the entire deque was drained, reset the head back to 0,- // like `.clear()` does.- source_deque.head = WrappedIndex::zero();- } else if head_len < tail_len {- // If we moved the head above, then we need to adjust the head index here.- source_deque.head = source_deque.to_wrapped_index(drain_len);- }- source_deque.len = new_len;+ if new_len == 0 {+ // Special case: If the entire deque was drained, reset the head back to 0,+ // like `.clear()` does.+ source_deque.head = WrappedIndex::zero();+ } else if head_len < tail_len {+ // If we moved the head above, then we need to adjust the head index here.+ source_deque.head = source_deque.to_wrapped_index(drain_len); }+ source_deque.len = new_len;+ });++ if mem::needs_drop::<T>() && guard.remaining != 0 {+ // SAFETY: We just checked that `self.remaining != 0`.+ let (front, back) = unsafe { guard.as_slices() };+ // since idx is a logical index, we don't need to worry about wrapping.+ guard.idx += front.len();+ guard.remaining -= front.len();+ // SAFETY: This can't have been dropped before since+ // `idx` & `remaining` track what's been dropped.+ unsafe { ptr::drop_in_place(front) };+ guard.remaining = 0;+ // SAFETY: Ditto.+ unsafe { ptr::drop_in_place(back) }; } } }library/alloc/src/collections/vec_deque/into_iter.rs19 + / 35 −
@@ -1,5 +1,5 @@ use core::iter::{FusedIterator, TrustedLen};-use core::mem::MaybeUninit;+use core::mem::{DropGuard, MaybeUninit}; use core::num::NonZero; use core::ops::Try; use core::{array, fmt, ptr};@@ -78,28 +78,20 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> { F: FnMut(B, Self::Item) -> R, R: Try<Output = B>, {- struct Guard<'a, T, A: Allocator> {- deque: &'a mut VecDeque<T, A>,- // `consumed <= deque.len` always holds.- consumed: usize,- }-- impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> {- fn drop(&mut self) {- self.deque.len -= self.consumed;- self.deque.head = self.deque.to_wrapped_index(self.consumed);- }- }-- let mut guard = Guard { deque: &mut self.inner, consumed: 0 };+ // `consumed <= deque.len` always holds.+ let mut guard = DropGuard::new((&mut self.inner, 0), |(deque, consumed)| {+ deque.len -= consumed;+ deque.head = deque.to_wrapped_index(consumed);+ }); - let (head, tail) = guard.deque.as_slices();+ let (deque, consumed) = &mut *guard;+ let (head, tail) = deque.as_slices(); init = head .iter() .map(|elem| {- guard.consumed += 1;- // SAFETY: Because we incremented `guard.consumed`, the+ *consumed += 1;+ // SAFETY: Because we incremented `consumed`, the // deque effectively forgot the element, so we can take // ownership unsafe { ptr::read(elem) }@@ -108,7 +100,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> { tail.iter() .map(|elem| {- guard.consumed += 1;+ *consumed += 1; // SAFETY: Same as above. unsafe { ptr::read(elem) } })@@ -201,34 +193,26 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> { F: FnMut(B, Self::Item) -> R, R: Try<Output = B>, {- struct Guard<'a, T, A: Allocator> {- deque: &'a mut VecDeque<T, A>,- // `consumed <= deque.len` always holds.- consumed: usize,- }-- impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> {- fn drop(&mut self) {- self.deque.len -= self.consumed;- }- }-- let mut guard = Guard { deque: &mut self.inner, consumed: 0 };+ // `consumed <= deque.len` always holds.+ let mut guard = DropGuard::new((&mut self.inner, 0), |(deque, consumed)| {+ deque.len -= consumed;+ }); - let (head, tail) = guard.deque.as_slices();+ let (deque, consumed) = &mut *guard;+ let (head, tail) = deque.as_slices(); init = tail .iter() .map(|elem| {- guard.consumed += 1;+ *consumed += 1; // SAFETY: See `try_fold`'s safety comment. unsafe { ptr::read(elem) } }) .try_rfold(init, &mut f)?; head.iter() .map(|elem| {- guard.consumed += 1;+ *consumed += 1; // SAFETY: Same as above. unsafe { ptr::read(elem) } })library/alloc/src/collections/vec_deque/mod.rs9 + / 21 −
@@ -17,7 +17,7 @@ use core::iter::{ByRefSized, repeat_n, repeat_with}; // failures in linkchecker even though rustdoc built the docs just fine. #[allow(unused_imports)] use core::mem;-use core::mem::{ManuallyDrop, SizedTypeProperties};+use core::mem::{DropGuard, ManuallyDrop, SizedTypeProperties}; use core::ops::{Index, IndexMut, Range, RangeBounds}; use core::{fmt, ptr, slice}; @@ -653,37 +653,25 @@ impl<T, A: Allocator> VecDeque<T, A> { mut iter: impl Iterator<Item = T>, len: usize, ) -> usize {- struct Guard<'a, T, A: Allocator> {- deque: &'a mut VecDeque<T, A>,- written: usize,- }-- impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> {- fn drop(&mut self) {- self.deque.len += self.written;- }- }- let head_room = self.capacity() - dst.as_index(); - let mut guard = Guard { deque: self, written: 0 };+ let mut guard = DropGuard::new((self, 0), |(deque, written)| {+ deque.len += written;+ });+ let (deque, written) = &mut *guard; if head_room >= len { // ignore-tidy-undocumented-unsafe- unsafe { guard.deque.write_iter(dst, iter, &mut guard.written) };+ unsafe { deque.write_iter(dst, iter, written) }; } else { // ignore-tidy-undocumented-unsafe unsafe {- guard.deque.write_iter(- dst,- ByRefSized(&mut iter).take(head_room),- &mut guard.written,- );- guard.deque.write_iter(WrappedIndex::zero(), iter, &mut guard.written)+ deque.write_iter(dst, ByRefSized(&mut iter).take(head_room), written);+ deque.write_iter(WrappedIndex::zero(), iter, written) }; } - guard.written+ *written } /// Frobs the head and tail sections around to handle the fact that welibrary/alloc/src/rc.rs13 + / 28 −
@@ -2450,47 +2450,32 @@ impl<T> Rc<[T]> { /// Behavior is undefined should the size be wrong. #[cfg(not(no_global_oom_handling))] unsafe fn from_iter_exact(iter: impl Iterator<Item = T>, len: usize) -> Rc<[T]> {- // Panic guard while cloning T elements.- // In the event of a panic, elements that have been written- // into the new RcInner will be dropped, then the memory freed.- struct Guard<T> {- mem: NonNull<u8>,- elems: *mut T,- layout: Layout,- n_elems: usize,- }-- impl<T> Drop for Guard<T> {- fn drop(&mut self) {- // ignore-tidy-undocumented-unsafe- unsafe {- let slice = from_raw_parts_mut(self.elems, self.n_elems);- ptr::drop_in_place(slice);-- Global.deallocate(self.mem, self.layout);- }- }- }+ use core::mem::DropGuard; // ignore-tidy-undocumented-unsafe unsafe { let ptr = Self::allocate_for_slice(len);-- let mem = ptr as *mut _ as *mut u8; let layout = Layout::for_value_raw(ptr); // Pointer to first element- let elems = (&raw mut (*ptr).value) as *mut T;+ let elems = (&raw mut (*ptr).value).as_mut_ptr(); - let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 };+ // Panic guard while cloning T elements.+ // In the event of a panic, elements that have been written+ // into the new RcInner will be dropped, then the memory freed.+ let mut guard = DropGuard::new(0, |n_elems| {+ let slice = from_raw_parts_mut(elems, n_elems);+ ptr::drop_in_place(slice);+ Global.deallocate(NonNull::new_unchecked(ptr.cast()), layout);+ }); for (i, item) in iter.enumerate() { ptr::write(elems.add(i), item);- guard.n_elems += 1;+ *guard += 1; } - // All clear. Forget the guard so it doesn't free the new RcInner.- mem::forget(guard);+ // All clear. Dismiss the guard so it doesn't free the new RcInner.+ DropGuard::dismiss(guard); Self::from_ptr(ptr) }library/alloc/src/slice.rs15 + / 20 −
@@ -408,35 +408,30 @@ impl<T> [T] { impl<T: Clone> ConvertVec for T { #[inline] default fn to_vec<A: Allocator>(s: &[Self], alloc: A) -> Vec<Self, A> {- struct DropGuard<'a, T, A: Allocator> {- vec: &'a mut Vec<T, A>,- num_init: usize,- }- impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> {- #[inline]- fn drop(&mut self) {+ use core::mem::DropGuard;++ let mut guard = DropGuard::new(+ (0, Vec::with_capacity_in(s.len(), alloc)),+ |(num_init, mut vec)| { // SAFETY: // items were marked initialized in the loop below- unsafe {- self.vec.set_len(self.num_init);- }- }- }- let mut vec = Vec::with_capacity_in(s.len(), alloc);- let mut guard = DropGuard { vec: &mut vec, num_init: 0 };- let slots = guard.vec.spare_capacity_mut();+ unsafe { vec.set_len(num_init) }+ },+ );+ let (num_init, vec) = &mut *guard;++ let slots = vec.spare_capacity_mut(); // .take(slots.len()) is necessary for LLVM to remove bounds checks // and has better codegen than zip. for (i, b) in s.iter().enumerate().take(slots.len()) {- guard.num_init = i;+ *num_init = i; slots[i].write(b.clone()); }- core::mem::forget(guard);++ let (_, mut vec) = DropGuard::dismiss(guard); // SAFETY: // the vec was allocated and initialized above to at least this length.- unsafe {- vec.set_len(s.len());- }+ unsafe { vec.set_len(s.len()) }; vec } }library/alloc/src/string.rs13 + / 20 −
@@ -46,6 +46,7 @@ use core::error::Error; use core::iter::FusedIterator; #[cfg(not(no_global_oom_handling))] use core::iter::from_fn;+use core::mem::DropGuard; #[cfg(not(no_global_oom_handling))] use core::num::Saturating; #[cfg(not(no_global_oom_handling))]@@ -1689,20 +1690,6 @@ impl String { return; } - struct PanicGuard<'a> {- s: &'a mut String,- write: usize,- }-- impl Drop for PanicGuard<'_> {- fn drop(&mut self) {- debug_assert!(self.write <= self.s.len());- debug_assert!(str::from_utf8(&self.s.vec[..self.write]).is_ok());- // SAFETY: Restore the string length to the number of bytes written so far.- unsafe { self.s.vec.set_len(self.write) }- }- }- // Fast path: find the first character that should be removed or return early. let mut chars = self.char_indices(); let (mut read, write) = loop {@@ -1714,26 +1701,32 @@ impl String { drop(chars); // Slow path: at least one character is going to be removed.- let mut g = PanicGuard { s: self, write };+ let mut guard = DropGuard::new((self, write), |(s, write)| {+ debug_assert!(write <= s.len());+ debug_assert!(str::from_utf8(&s.vec[..write]).is_ok());+ // SAFETY: Restore the string length to the number of bytes written so far.+ unsafe { s.vec.set_len(write) }+ });+ let (s, write) = &mut *guard; while read < len { // SAFETY: `read` is within bound because `read` < `len`, so taking // a slice with `len` is safe.- let ch = unsafe { g.s.get_unchecked(read..len).chars().next().unwrap_unchecked() };+ let ch = unsafe { s.get_unchecked(read..len).chars().next().unwrap_unchecked() }; let ch_len = ch.len_utf8(); if f(ch) { // SAFETY: `read` is on a char boundary, as guaranteed above; `g.write` is // within bounds because it is always behind `read`. unsafe {- let ptr = g.s.vec.as_mut_ptr();- ptr::copy(ptr.add(read), ptr.add(g.write), ch_len);+ let ptr = s.vec.as_mut_ptr();+ ptr::copy(ptr.add(read), ptr.add(*write), ch_len); }- g.write += ch_len;+ *write += ch_len; } read += ch_len; } // All bytes processed; commit the final length by dropping the guard.- drop(g);+ drop(guard); } /// Inserts a character into this `String` at byte position `idx`.library/alloc/src/sync.rs17 + / 39 −
@@ -19,6 +19,8 @@ use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize};+#[cfg(not(no_global_oom_handling))]+use core::mem::DropGuard; use core::mem::{self, Alignment, ManuallyDrop}; use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver};@@ -2417,47 +2419,31 @@ impl<T> Arc<[T]> { /// Behavior is undefined should the size be wrong. #[cfg(not(no_global_oom_handling))] unsafe fn from_iter_exact(iter: impl Iterator<Item = T>, len: usize) -> Arc<[T]> {- // Panic guard while cloning T elements.- // In the event of a panic, elements that have been written- // into the new ArcInner will be dropped, then the memory freed.- struct Guard<T> {- mem: NonNull<u8>,- elems: *mut T,- layout: Layout,- n_elems: usize,- }-- impl<T> Drop for Guard<T> {- fn drop(&mut self) {- // ignore-tidy-undocumented-unsafe- unsafe {- let slice = from_raw_parts_mut(self.elems, self.n_elems);- ptr::drop_in_place(slice);-- Global.deallocate(self.mem, self.layout);- }- }- }- // ignore-tidy-undocumented-unsafe unsafe { let ptr = Self::allocate_for_slice(len);-- let mem = ptr as *mut _ as *mut u8; let layout = Layout::for_value_raw(ptr); // Pointer to first element- let elems = (&raw mut (*ptr).data) as *mut T;+ let elems = (&raw mut (*ptr).data).as_mut_ptr();++ // Panic guard while cloning T elements.+ // In the event of a panic, elements that have been written+ // into the new ArcInner will be dropped, then the memory freed.+ let mut guard = DropGuard::new(0, |n_elems| {+ let slice = from_raw_parts_mut(elems, n_elems);+ ptr::drop_in_place(slice); - let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 };+ Global.deallocate(NonNull::new_unchecked(ptr.cast()), layout);+ }); for (i, item) in iter.enumerate() { ptr::write(elems.add(i), item);- guard.n_elems += 1;+ *guard += 1; } - // All clear. Forget the guard so it doesn't free the new ArcInner.- mem::forget(guard);+ // All clear. Dismiss the guard so it doesn't free the new ArcInner.+ DropGuard::dismiss(guard); Self::from_ptr(ptr) }@@ -2678,15 +2664,7 @@ impl<T: ?Sized + CloneToUninit, A: AllocatorClone> Arc<T, A> { // If we unwind before the Arc is overwritten, we expose a strong // count of 0, resulting in a UAF (#155746, #157203). // Until the new Arc is written, the old Arc must remain valid- struct Guard<'a, T: ?Sized> {- inner: &'a ArcInner<T>,- }- impl<'a, T: ?Sized> Drop for Guard<'a, T> {- fn drop(&mut self) {- self.inner.strong.store(1, Release);- }- }- let guard = Guard { inner: this.inner() };+ let guard = DropGuard::new(this.inner(), |inner| inner.strong.store(1, Release)); // Can just steal the data, all that's left is Weaks // Note that this can panic in two ways:@@ -2707,7 +2685,7 @@ impl<T: ?Sized + CloneToUninit, A: AllocatorClone> Arc<T, A> { ); // We are now safe from panics.- mem::forget(guard);+ DropGuard::dismiss(guard); // Materialize our own implicit weak pointer, so that it can clean // up the ArcInner as needed.library/alloc/src/vec/drain.rs18 + / 25 −
@@ -1,5 +1,5 @@ use core::iter::{FusedIterator, TrustedLen};-use core::mem::{self, ManuallyDrop, SizedTypeProperties};+use core::mem::{self, DropGuard, ManuallyDrop, SizedTypeProperties}; use core::ptr::{self, NonNull}; use core::{fmt, slice}; @@ -176,29 +176,6 @@ impl<T, A: Allocator> DoubleEndedIterator for Drain<'_, T, A> { #[stable(feature = "drain", since = "1.6.0")] impl<T, A: Allocator> Drop for Drain<'_, T, A> { fn drop(&mut self) {- /// Moves back the un-`Drain`ed elements to restore the original `Vec`.- struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>);-- impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> {- fn drop(&mut self) {- if self.0.tail_len > 0 {- // ignore-tidy-undocumented-unsafe- unsafe {- let source_vec = self.0.vec.as_mut();- // memmove back untouched tail, update to new length- let start = source_vec.len();- let tail = self.0.tail_start;- if tail != start {- let src = source_vec.as_ptr().add(tail);- let dst = source_vec.as_mut_ptr().add(start);- ptr::copy(src, dst, self.0.tail_len);- }- source_vec.set_len(start + self.0.tail_len);- }- }- }- }- let iter = mem::take(&mut self.iter); let drop_len = iter.len(); @@ -219,7 +196,23 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> { } // ensure elements are moved back into their appropriate places, even when drop_in_place panics- let _guard = DropGuard(self);+ let _guard = DropGuard::new(self, |this| {+ if this.tail_len > 0 {+ // ignore-tidy-undocumented-unsafe+ unsafe {+ let source_vec = this.vec.as_mut();+ // memmove back untouched tail, update to new length+ let start = source_vec.len();+ let tail = this.tail_start;+ if tail != start {+ let src = source_vec.as_ptr().add(tail);+ let dst = source_vec.as_mut_ptr().add(start);+ ptr::copy(src, dst, this.tail_len);+ }+ source_vec.set_len(start + this.tail_len);+ }+ }+ }); if drop_len == 0 { return;library/alloc/src/vec/into_iter.rs4 + / 16 −
@@ -3,7 +3,7 @@ use core::iter::{ TrustedRandomAccessNoCoerce, }; use core::marker::PhantomData;-use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties};+use core::mem::{DropGuard, ManuallyDrop, MaybeUninit, SizedTypeProperties}; use core::num::NonZero; #[cfg(not(no_global_oom_handling))] use core::ops::Deref;@@ -589,23 +589,11 @@ impl<T: Clone, A: Allocator + Clone> Clone for IntoIter<T, A> { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter<T, A> { fn drop(&mut self) {- struct DropGuard<'a, T, A: Allocator>(&'a mut IntoIter<T, A>);-- impl<T, A: Allocator> Drop for DropGuard<'_, T, A> {- fn drop(&mut self) {- // ignore-tidy-undocumented-unsafe- unsafe {- self.0.dealloc_only();- }- }- }-- let guard = DropGuard(self);+ // ignore-tidy-undocumented-unsafe+ let mut guard = DropGuard::new(self, |this| unsafe { this.dealloc_only() }); // destroy the remaining elements // ignore-tidy-undocumented-unsafe- unsafe {- ptr::drop_in_place(guard.0.as_raw_mut_slice());- }+ unsafe { ptr::drop_in_place(guard.as_raw_mut_slice()) } // now `guard` will be dropped and do the rest } }library/std/src/sys/fs/unix.rs14 + / 22 −
@@ -2299,19 +2299,6 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { #[cfg(target_vendor = "apple")] pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA;-- struct FreeOnDrop(libc::copyfile_state_t);- impl Drop for FreeOnDrop {- fn drop(&mut self) {- // The code below ensures that `FreeOnDrop` is never a null pointer- unsafe {- // `copyfile_state_free` returns -1 if the `to` or `from` files- // cannot be closed. However, this is not considered an error.- libc::copyfile_state_free(self.0);- }- }- }- let (reader, reader_metadata) = open_from(from)?; let clonefile_result = run_path_with_cstr(to, &|to| {@@ -2332,24 +2319,29 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?; - // We ensure that `FreeOnDrop` never contains a null pointer so it is+ let state = unsafe { libc::copyfile_state_alloc() };+ // We ensure that the guard never contains a null pointer so it is // always safe to call `copyfile_state_free`- let state = unsafe {- let state = libc::copyfile_state_alloc();- if state.is_null() {- return Err(crate::io::Error::last_os_error());+ if state.is_null() {+ return Err(crate::io::Error::last_os_error());+ }+ let state = crate::mem::DropGuard::new(state, |state| {+ // SAFETY: just checked it's not null+ unsafe {+ // `copyfile_state_free` returns -1 if the `to` or `from` files+ // cannot be closed. However, this is not considered an error.+ libc::copyfile_state_free(state); }- FreeOnDrop(state)- };+ }); let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { libc::COPYFILE_DATA }; - cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;+ cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), *state, flags) })?; let mut bytes_copied: libc::off_t = 0; cvt(unsafe { libc::copyfile_state_get(- state.0,+ *state, libc::COPYFILE_STATE_COPIED as u32, (&raw mut bytes_copied) as *mut libc::c_void, )library/std/src/sys/pal/unix/sync/condvar.rs8 + / 13 −
@@ -151,28 +151,23 @@ impl Condvar { /// # Safety /// May only be called once per instance of `Self`. pub unsafe fn init(self: Pin<&mut Self>) {+ use crate::mem::DropGuard; use crate::pin::pin; - struct AttrGuard<'a>(Pin<&'a COpaque<libc::pthread_condattr_t>>);- impl Drop for AttrGuard<'_> {- fn drop(&mut self) {- unsafe {- let result = libc::pthread_condattr_destroy(self.0.get());- assert_eq!(result, 0);- }- }- }- unsafe { let attr = pin!(COpaque::<libc::pthread_condattr_t>::uninit());+ // FIXME(pin-ergonomics): remove the next line. let attr = attr.into_ref(); let r = libc::pthread_condattr_init(attr.get()); assert_eq!(r, 0);- let attr = AttrGuard(attr);- let r = libc::pthread_condattr_setclock(attr.0.get(), Self::CLOCK);+ let attr = DropGuard::new(attr, |attr| {+ let result = libc::pthread_condattr_destroy(attr.get());+ assert_eq!(result, 0);+ });+ let r = libc::pthread_condattr_setclock(attr.get(), Self::CLOCK); assert_eq!(r, 0);- let r = libc::pthread_cond_init(self.as_ref().raw(), attr.0.get());+ let r = libc::pthread_cond_init(self.as_ref().raw(), attr.get()); assert_eq!(r, 0); } }library/std/src/sys/process/unix/unix.rs23 + / 47 −
@@ -394,19 +394,11 @@ impl Command { // want to be sure to restore the global environment back to what it // once was, ensuring that our temporary override, when free'd, doesn't // corrupt our process's environment.- let mut _reset = None;+ let _reset; if let Some(envp) = maybe_envp {- struct Reset(*const *const libc::c_char);-- impl Drop for Reset {- fn drop(&mut self) {- unsafe {- *sys::env::environ() = self.0;- }- }- }-- _reset = Some(Reset(*sys::env::environ()));+ _reset = core::mem::DropGuard::new(*sys::env::environ(), |prev| {+ *sys::env::environ() = prev;+ }); *sys::env::environ() = envp.as_ptr(); } @@ -461,8 +453,8 @@ impl Command { #[cfg(target_os = "linux")] use core::sync::atomic::{Atomic, AtomicU8, Ordering}; - use crate::mem::MaybeUninit;- use crate::pin::{Pin, pin};+ use crate::mem::{DropGuard, MaybeUninit};+ use crate::pin::pin; use crate::sys::helpers::COpaque; use crate::sys::{self, cvt_nz, on_broken_pipe_used}; @@ -679,68 +671,52 @@ impl Command { let pgroup = self.get_pgroup(); - struct PosixSpawnFileActions<'a>(Pin<&'a COpaque<libc::posix_spawn_file_actions_t>>);-- impl Drop for PosixSpawnFileActions<'_> {- fn drop(&mut self) {- unsafe {- libc::posix_spawn_file_actions_destroy(self.0.get());- }- }- }-- struct PosixSpawnattr<'a>(Pin<&'a COpaque<libc::posix_spawnattr_t>>);-- impl Drop for PosixSpawnattr<'_> {- fn drop(&mut self) {- unsafe {- libc::posix_spawnattr_destroy(self.0.get());- }- }- }- unsafe { let attrs = pin!(COpaque::uninit()); // FIXME(pin-ergonomics): remove the next line. let attrs = attrs.into_ref(); cvt_nz(libc::posix_spawnattr_init(attrs.get()))?;- let attrs = PosixSpawnattr(attrs);+ let attrs = DropGuard::new(attrs, |attrs| {+ libc::posix_spawnattr_destroy(attrs.get());+ }); let mut flags = 0; let file_actions = pin!(COpaque::uninit()); let file_actions = file_actions.into_ref(); cvt_nz(libc::posix_spawn_file_actions_init(file_actions.get()))?;- let file_actions = PosixSpawnFileActions(file_actions);+ let file_actions = DropGuard::new(file_actions, |file_actions| {+ libc::posix_spawn_file_actions_destroy(file_actions.get());+ }); if let Some(fd) = stdio.stdin.fd() { cvt_nz(libc::posix_spawn_file_actions_adddup2(- file_actions.0.get(),+ file_actions.get(), fd, libc::STDIN_FILENO, ))?; } if let Some(fd) = stdio.stdout.fd() { cvt_nz(libc::posix_spawn_file_actions_adddup2(- file_actions.0.get(),+ file_actions.get(), fd, libc::STDOUT_FILENO, ))?; } if let Some(fd) = stdio.stderr.fd() { cvt_nz(libc::posix_spawn_file_actions_adddup2(- file_actions.0.get(),+ file_actions.get(), fd, libc::STDERR_FILENO, ))?; } if let Some((f, cwd)) = addchdir {- cvt_nz(f(file_actions.0.get(), cwd.as_ptr()))?;+ cvt_nz(f(file_actions.get(), cwd.as_ptr()))?; } if let Some(pgroup) = pgroup { flags |= libc::POSIX_SPAWN_SETPGROUP;- cvt_nz(libc::posix_spawnattr_setpgroup(attrs.0.get(), pgroup))?;+ cvt_nz(libc::posix_spawnattr_setpgroup(attrs.get(), pgroup))?; } // Inherit the signal mask from this process rather than resetting it (i.e. do not call@@ -758,7 +734,7 @@ impl Command { { cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGLOST))?; }- cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.0.get(), default_set.as_ptr()))?;+ cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.get(), default_set.as_ptr()))?; flags |= libc::POSIX_SPAWN_SETSIGDEF; } @@ -773,7 +749,7 @@ impl Command { } } - cvt_nz(libc::posix_spawnattr_setflags(attrs.0.get(), flags as _))?;+ cvt_nz(libc::posix_spawnattr_setflags(attrs.get(), flags as _))?; // Make sure we synchronize access to the global `environ` resource let _env_lock = sys::env::env_read_lock();@@ -790,8 +766,8 @@ impl Command { let spawn_res = pidfd_spawnp.get().unwrap()( &mut pidfd, self.get_program_cstr().as_ptr(),- file_actions.0.get(),- attrs.0.get(),+ file_actions.get(),+ attrs.get(), self.get_argv().as_ptr() as *const _, envp as *const _, );@@ -832,8 +808,8 @@ impl Command { let spawn_res = spawn_fn( &mut p.pid, self.get_program_cstr().as_ptr(),- file_actions.0.get(),- attrs.0.get(),+ file_actions.get(),+ attrs.get(), self.get_argv().as_ptr() as *const _, envp as *const _, );library/std/src/sys/process/windows/tests.rs4 + / 8 −
@@ -1,6 +1,7 @@ use super::child_pipe::{Pipes, child_pipe}; use super::{Arg, make_command_line}; use crate::ffi::{OsStr, OsString};+use crate::mem::DropGuard; use crate::os::windows::io::AsHandle; use crate::process::{Command, Stdio}; use crate::time::Duration;@@ -36,14 +37,9 @@ fn test_thread_handle() { assert!(p.is_ok()); // Ensure the process is killed in the event something goes wrong.- struct DropGuard(crate::process::Child);- impl Drop for DropGuard {- fn drop(&mut self) {- let _ = self.0.kill();- }- }- let mut p = DropGuard(p.unwrap());- let p = &mut p.0;+ let mut p = DropGuard::new(p.unwrap(), |mut p| {+ let _: Result<(), crate::io::Error> = p.kill();+ }); unsafe extern "system" { unsafe fn ResumeThread(hHandle: BorrowedHandle<'_>) -> u32;