Change:
<!-- Thank you for filing a regression report! A regression is something that changed between versions of Rust but was not supposed to. Please provide a short summary of the regression, along with any information you feel is relevant to replicate it. --I tried this code: I expected to see this happen: I expected to see the struct in the Sender buffer get dropped after the Receiver is dropped during the unwind of the panicking thread. Instead, this happened: The data in the Sender buffer is not dropped until the whole process ends. <!-- Provide the most recent version this worked on, for example: It most recently worked on: Rust 1.47 --It most recently worked on: 1.66.1 <!-- Provide the version you are using that has the regression. --: <!-- Did the compiler crash? If so, please provide a backtrace. --</p</details<!-- If you know when this regression occurred, please add a line like below, replacing with one of stable, beta, or nightly. modify labels: +regression-from-stable-to-{channel} -regression-untriaged --
bisection seems to point to cc searched nightlies: from nightly-2022-11-01 to nightly-2023-02-01 regressed nightly: nightly-2022-11-14 searched commit range: regressed commit: <details<summarybisected with <a href=''cargo-bisect-rustc</av0.6.5</summaryHost triple: x86_64-unknown-linux-gnu Reproduce with:
In crossbeam, the unbounded channel does this (), but the bounded channel does not. There is no particular intent, it just hasn't been implemented. cc

Discussion:
/// The current stamp. stamp: AtomicUsize, <del> /// The message in this slot. </del> <ins> /// The message in this slot. Either read out in `read` or dropped through /// `discard_all_messages`. </ins> msg: UnsafeCell<MaybeUninit<T>>, }

Some(self.cap) } <del> /// Disconnects the channel and wakes up all blocked senders and receivers. </del> <ins> /// Disconnects senders and wakes up all blocked receivers. </ins> /// /// Returns `true` if this call disconnected the channel. <del> pub(crate) fn disconnect(&self) -> bool { </del> <ins> pub(crate) fn disconnect_senders(&self) -> bool { </ins> let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst); if tail & self.mark_bit == 0 { <del> self.senders.disconnect(); </del> self.receivers.disconnect(); true } else {

} } <ins> /// Disconnects receivers and wakes up all blocked senders. /// /// Returns `true` if this call disconnected the channel. /// /// # Safety /// May only be called once upon dropping the last receiver. The /// destruction of all other receivers must have been observed with acquire /// ordering or stronger. pub(crate) unsafe fn disconnect_receivers(&self) -> bool { let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst); let disconnected = if tail & self.mark_bit == 0 { self.senders.disconnect(); true } else { false }; self.discard_all_messages(tail); disconnected } /// Discards all messages. /// /// `tail` should be the current (and therefore last) value of `tail`. /// /// # Panicking /// If a destructor panics, the remaining messages are leaked, matching the /// behaviour of the unbounded channel. /// /// # Safety /// This method must only be called when dropping the last receiver. The /// destruction of all other receivers must have been observed with acquire /// ordering or stronger. unsafe fn discard_all_messages(&self, tail: usize) { debug_assert!(self.is_disconnected()); // Only receivers modify `head`, so since we are the last one, // this value will not change and will not be observed (since // no new messages can be sent after disconnection). let mut head = self.head.load(Ordering::Relaxed); let tail = tail & !self.mark_bit; let backoff = Backoff::new(); loop { // Deconstruct the head. let index = head & (self.mark_bit - 1); let lap = head & !(self.one_lap - 1); // Inspect the corresponding slot. debug_assert!(index < self.buffer.len()); let slot = unsafe { self.buffer.get_unchecked(index) }; let stamp = slot.stamp.load(Ordering::Acquire); // If the stamp is ahead of the head by 1, we may drop the message. if head + 1 == stamp { head = if index + 1 < self.cap { // Same lap, incremented index. // Set to `{ lap: lap, mark: 0, index: index + 1 }`. head + 1 } else { // One lap forward, index wraps around to zero. // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. lap.wrapping_add(self.one_lap) }; unsafe { (*slot.msg.get()).assume_init_drop(); } // If the tail equals the head, that means the channel is empty. } else if tail == head { return; // Otherwise, a sender is about to write into the slot, so we need // to wait for it to update the stamp. } else { backoff.spin_heavy(); } } } </ins> /// Returns `true` if the channel is disconnected. pub(crate) fn is_disconnected(&self) -> bool { self.tail.load(Ordering::SeqCst) & self.mark_bit != 0

head.wrapping_add(self.one_lap) == tail & !self.mark_bit } } <del> impl<T> Drop for Channel<T> { fn drop(&mut self) { // Get the index of the head. let hix = self.head.load(Ordering::Relaxed) & (self.mark_bit - 1); // Loop over all slots that hold a message and drop them. for i in 0..self.len() { // Compute the index of the next slot holding a message. let index = if hix + i < self.cap { hix + i } else { hix + i - self.cap }; unsafe { debug_assert!(index < self.buffer.len()); let slot = self.buffer.get_unchecked_mut(index); let msg = &mut *slot.msg.get(); msg.as_mut_ptr().drop_in_place(); } } } } </del>

fn drop(&mut self) { unsafe { match &self.flavor { <del> SenderFlavor::Array(chan) => chan.release(|c| c.disconnect()), </del> <ins> SenderFlavor::Array(chan) => chan.release(|c| c.disconnect_senders()), </ins> SenderFlavor::List(chan) => chan.release(|c| c.disconnect_senders()), SenderFlavor::Zero(chan) => chan.release(|c| c.disconnect()), }

fn drop(&mut self) { unsafe { match &self.flavor { <del> ReceiverFlavor::Array(chan) => chan.release(|c| c.disconnect()), </del> <ins> ReceiverFlavor::Array(chan) => chan.release(|c| c.disconnect_receivers()), </ins> ReceiverFlavor::List(chan) => chan.release(|c| c.disconnect_receivers()), ReceiverFlavor::Zero(chan) => chan.release(|c| c.disconnect()), }

use super::*; use crate::env; <ins> use crate::rc::Rc; </ins> use crate::sync::mpmc::SendTimeoutError; use crate::thread; use crate::time::Duration;

repro() } } <ins> #[test] fn drop_unreceived() { let (tx, rx) = sync_channel::<Rc<()>>(1); let msg = Rc::new(()); let weak = Rc::downgrade(&msg); assert!(tx.send(msg).is_ok()); drop(rx); // Messages should be dropped immediately when the last receiver is destroyed. assert!(weak.upgrade().is_none()); drop(tx); } </ins>