diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index d13cd3fd1dab0..ea86dfbd9c80e 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -26,16 +26,6 @@ pub trait PrintAttribute { fn print_attribute(&self, p: &mut Printer); } -impl PrintAttribute for u128 { - fn should_render(&self) -> bool { - true - } - - fn print_attribute(&self, p: &mut Printer) { - p.word(self.to_string()) - } -} - impl PrintAttribute for &T { fn should_render(&self) -> bool { T::should_render(self) @@ -148,7 +138,7 @@ macro_rules! print_tup { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed); -print_disp!(u16, bool, NonZero, Limit); +print_disp!(u16, u128, bool, NonZero, Limit); print_debug!( Symbol, Ident, diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index d5a184c259319..2de892fa797ab 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -160,7 +160,7 @@ impl DenseBitSet { /// Count the number of set bits in the set. pub fn count(&self) -> usize { - self.words.iter().map(|e| e.count_ones() as usize).sum() + count_ones(&self.words) } /// Returns `true` if `self` contains `elem`. @@ -786,7 +786,7 @@ impl BitRelations> for ChunkedBitSet { match (&mut self_chunk, &other_chunk) { (_, Zeros) | (Ones, _) => {} - (Zeros, Ones) | (Mixed(..), Ones) | (Zeros, Mixed(..)) => { + (Zeros, _) | (Mixed(..), Ones) => { // `other_chunk` fully overwrites `self_chunk` *self_chunk = other_chunk.clone(); changed = true; @@ -814,10 +814,8 @@ impl BitRelations> for ChunkedBitSet { op, ); debug_assert!(has_changed); - *self_chunk_count = self_chunk_words[0..num_words] - .iter() - .map(|w| w.count_ones() as ChunkSize) - .sum(); + *self_chunk_count = + count_ones(&self_chunk_words[0..num_words]) as ChunkSize; if *self_chunk_count == chunk_domain_size { *self_chunk = Ones; } @@ -850,7 +848,7 @@ impl BitRelations> for ChunkedBitSet { match (&mut self_chunk, &other_chunk) { (Zeros, _) | (_, Zeros) => {} - (Ones | Mixed(_, _), Ones) => { + (Ones | Mixed(..), Ones) => { changed = true; *self_chunk = Zeros; } @@ -868,10 +866,7 @@ impl BitRelations> for ChunkedBitSet { let self_chunk_count = chunk_domain_size - *other_chunk_count; debug_assert_eq!( self_chunk_count, - self_chunk_words[0..num_words] - .iter() - .map(|w| w.count_ones() as ChunkSize) - .sum() + count_ones(&self_chunk_words[0..num_words]) as ChunkSize ); *self_chunk = Mixed(self_chunk_count, Rc::new(self_chunk_words)); } @@ -894,10 +889,8 @@ impl BitRelations> for ChunkedBitSet { op, ); debug_assert!(has_changed); - *self_chunk_count = self_chunk_words[0..num_words] - .iter() - .map(|w| w.count_ones() as ChunkSize) - .sum(); + *self_chunk_count = + count_ones(&self_chunk_words[0..num_words]) as ChunkSize; if *self_chunk_count == 0 { *self_chunk = Zeros; } @@ -953,10 +946,8 @@ impl BitRelations> for ChunkedBitSet { op, ); debug_assert!(has_changed); - *self_chunk_count = self_chunk_words[0..num_words] - .iter() - .map(|w| w.count_ones() as ChunkSize) - .sum(); + *self_chunk_count = + count_ones(&self_chunk_words[0..num_words]) as ChunkSize; if *self_chunk_count == 0 { *self_chunk = Zeros; } @@ -970,48 +961,6 @@ impl BitRelations> for ChunkedBitSet { } } -impl BitRelations> for DenseBitSet { - fn union(&mut self, other: &ChunkedBitSet) -> bool { - sequential_update(|elem| self.insert(elem), other.iter()) - } - - fn subtract(&mut self, _other: &ChunkedBitSet) -> bool { - unimplemented!("implement if/when necessary"); - } - - fn intersect(&mut self, other: &ChunkedBitSet) -> bool { - assert_eq!(self.domain_size(), other.domain_size); - let mut changed = false; - for (i, chunk) in other.chunks.iter().enumerate() { - let mut words = &mut self.words[i * CHUNK_WORDS..]; - if words.len() > CHUNK_WORDS { - words = &mut words[..CHUNK_WORDS]; - } - match chunk { - Zeros => { - for word in words { - if *word != 0 { - changed = true; - *word = 0; - } - } - } - Ones => (), - Mixed(_, data) => { - for (i, word) in words.iter_mut().enumerate() { - let new_val = *word & data[i]; - if new_val != *word { - changed = true; - *word = new_val; - } - } - } - } - } - changed - } -} - impl Clone for ChunkedBitSet { fn clone(&self) -> Self { ChunkedBitSet { @@ -1085,21 +1034,12 @@ impl Chunk { assert!(0 < count && count < chunk_domain_size); // Check the number of set bits matches `count`. - assert_eq!( - words.iter().map(|w| w.count_ones() as ChunkSize).sum::(), - count - ); + assert_eq!(count_ones(words.as_slice()) as ChunkSize, count); // Check the not-in-use words are all zeroed. let num_words = num_words(chunk_domain_size as usize); if num_words < CHUNK_WORDS { - assert_eq!( - words[num_words..] - .iter() - .map(|w| w.count_ones() as ChunkSize) - .sum::(), - 0 - ); + assert_eq!(count_ones(&words[num_words..]) as ChunkSize, 0); } } } @@ -1122,15 +1062,6 @@ enum ChunkIter<'a> { Finished, } -// Applies a function to mutate a bitset, and returns true if any -// of the applications return true -fn sequential_update( - mut self_update: impl FnMut(T) -> bool, - it: impl Iterator, -) -> bool { - it.fold(false, |changed, elem| self_update(elem) | changed) -} - impl fmt::Debug for ChunkedBitSet { fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { w.debug_list().entries(self.iter()).finish() @@ -1590,7 +1521,7 @@ impl BitMatrix { /// Returns the number of elements in `row`. pub fn count(&self, row: R) -> usize { let (start, end) = self.range(row); - self.words[start..end].iter().map(|e| e.count_ones() as usize).sum() + count_ones(&self.words[start..end]) } } @@ -1801,6 +1732,11 @@ fn max_bit(word: Word) -> usize { WORD_BITS - 1 - word.leading_zeros() as usize } +#[inline] +fn count_ones(words: &[Word]) -> usize { + words.iter().map(|word| word.count_ones() as usize).sum() +} + /// Integral type used to represent the bit set. pub trait FiniteBitSetTy: BitAnd diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs index deddc87261436..341e0622df75e 100644 --- a/compiler/rustc_index/src/bit_set/tests.rs +++ b/compiler/rustc_index/src/bit_set/tests.rs @@ -306,34 +306,6 @@ fn with_elements_chunked(elements: &[usize], domain_size: usize) -> ChunkedBitSe s } -fn with_elements_standard(elements: &[usize], domain_size: usize) -> DenseBitSet { - let mut s = DenseBitSet::new_empty(domain_size); - for &e in elements { - assert!(s.insert(e)); - } - s -} - -#[test] -fn chunked_bitset_into_bitset_operations() { - let a = vec![1, 5, 7, 11, 15, 2000, 3000]; - let b = vec![3, 4, 11, 3000, 4000]; - let aub = vec![1, 3, 4, 5, 7, 11, 15, 2000, 3000, 4000]; - let aib = vec![11, 3000]; - - let b = with_elements_chunked(&b, 9876); - - let mut union = with_elements_standard(&a, 9876); - assert!(union.union(&b)); - assert!(!union.union(&b)); - assert!(union.iter().eq(aub.iter().copied())); - - let mut intersection = with_elements_standard(&a, 9876); - assert!(intersection.intersect(&b)); - assert!(!intersection.intersect(&b)); - assert!(intersection.iter().eq(aib.iter().copied())); -} - #[test] fn chunked_bitset_iter() { fn check_iter(bit: &ChunkedBitSet, vec: &Vec) { diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs index d296ae46f43e7..ae2265b5d96b2 100644 --- a/compiler/rustc_lint/src/shadowed_into_iter.rs +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -134,6 +134,8 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter { && let hir::ExprKind::Call(path, [_]) = &arg.kind && let hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoIterIntoIter, ..)) = &path.kind + && !receiver_arg.span.from_expansion() + && !expr.span.from_expansion() { Some(ShadowedIntoIterDiagSub::RemoveIntoIter { span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), diff --git a/library/Cargo.lock b/library/Cargo.lock index 47fbf5169f491..b5b534c986b99 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -139,9 +139,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.175" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" dependencies = [ "rustc-std-workspace-core", ] diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index 8868f05f1b98f..334a4b7119a11 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -1,3 +1,4 @@ +use crate::marker::Destruct; use crate::ops::{Deref, DerefMut, DerefPure}; use crate::ptr; @@ -249,7 +250,11 @@ impl ManuallyDrop { /// [pinned]: crate::pin #[stable(feature = "manually_drop", since = "1.20.0")] #[inline] - pub unsafe fn drop(slot: &mut ManuallyDrop) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn drop(slot: &mut ManuallyDrop) + where + T: [const] Destruct, + { // SAFETY: we are dropping the value pointed to by a mutable reference // which is guaranteed to be valid for writes. // It is up to the caller to make sure that `slot` isn't dropped again. diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index c160360cfacf9..8d666e9b130c0 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1,4 +1,5 @@ use crate::any::type_name; +use crate::marker::Destruct; use crate::mem::ManuallyDrop; use crate::{fmt, intrinsics, ptr, slice}; @@ -714,7 +715,11 @@ impl MaybeUninit { /// /// [`assume_init`]: MaybeUninit::assume_init #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] - pub unsafe fn assume_init_drop(&mut self) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn assume_init_drop(&mut self) + where + T: [const] Destruct, + { // SAFETY: the caller must guarantee that `self` is initialized and // satisfies all invariants of `T`. // Dropping the value in place is safe if that is the case. @@ -1390,7 +1395,11 @@ impl [MaybeUninit] { /// behaviour. #[unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] - pub unsafe fn assume_init_drop(&mut self) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn assume_init_drop(&mut self) + where + T: [const] Destruct, + { if !self.is_empty() { // SAFETY: the caller must guarantee that every element of `self` // is initialized and satisfies all invariants of `T`. diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs index a2ae034e55e66..95b49a38ded06 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/bignum.rs @@ -38,9 +38,8 @@ macro_rules! impl_full_ops { fn full_mul_add(self, other: $ty, other2: $ty, carry: $ty) -> ($ty, $ty) { // This cannot overflow; // the output is between `0` and `2^nbits * (2^nbits - 1)`. - let v = (self as $bigty) * (other as $bigty) + (other2 as $bigty) + - (carry as $bigty); - ((v >> <$ty>::BITS) as $ty, v as $ty) + let (lo, hi) = self.carrying_mul_add(other, other2, carry); + (hi, lo) } fn full_div_rem(self, other: $ty, borrow: $ty) -> ($ty, $ty) { diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index b29d267654252..fd067d19fcd98 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -403,7 +403,7 @@ use crate::cmp::Ordering; use crate::intrinsics::const_eval_select; -use crate::marker::{FnPtr, PointeeSized}; +use crate::marker::{Destruct, FnPtr, PointeeSized}; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::{fmt, hash, intrinsics, ub_checks}; @@ -801,7 +801,11 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { #[lang = "drop_in_place"] #[allow(unconditional_recursion)] #[rustc_diagnostic_item = "ptr_drop_in_place"] -pub unsafe fn drop_in_place(to_drop: *mut T) { +#[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] +pub const unsafe fn drop_in_place(to_drop: *mut T) +where + T: [const] Destruct, +{ // Code here does not matter - this is replaced by the // real drop glue by the compiler. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index ba78afc7ea114..24ee92bdd6e1b 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1,7 +1,7 @@ use super::*; use crate::cmp::Ordering::{Equal, Greater, Less}; use crate::intrinsics::const_eval_select; -use crate::marker::PointeeSized; +use crate::marker::{Destruct, PointeeSized}; use crate::mem::{self, SizedTypeProperties}; use crate::slice::{self, SliceIndex}; @@ -1390,8 +1390,12 @@ impl *mut T { /// /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] #[inline(always)] - pub unsafe fn drop_in_place(self) { + pub const unsafe fn drop_in_place(self) + where + T: [const] Destruct, + { // SAFETY: the caller must uphold the safety contract for `drop_in_place`. unsafe { drop_in_place(self) } } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 10f83120428b9..a762e969b52dc 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1,5 +1,5 @@ use crate::cmp::Ordering; -use crate::marker::{PointeeSized, Unsize}; +use crate::marker::{Destruct, PointeeSized, Unsize}; use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{CoerceUnsized, DispatchFromDyn}; @@ -1118,7 +1118,11 @@ impl NonNull { /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place() #[inline(always)] #[stable(feature = "non_null_convenience", since = "1.80.0")] - pub unsafe fn drop_in_place(self) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn drop_in_place(self) + where + T: [const] Destruct, + { // SAFETY: the caller must uphold the safety contract for `drop_in_place`. unsafe { ptr::drop_in_place(self.as_ptr()) } } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index a80d7f8b44d7d..c2dc3a99ab109 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -19,6 +19,7 @@ #![feature(const_cmp)] #![feature(const_convert)] #![feature(const_destruct)] +#![feature(const_drop_in_place)] #![feature(const_eval_select)] #![feature(const_mul_add)] #![feature(const_ops)] diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index 4d5138d539b95..e89f21271027a 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -1,6 +1,6 @@ use core::cell::RefCell; use core::marker::Freeze; -use core::mem::MaybeUninit; +use core::mem::{ManuallyDrop, MaybeUninit}; use core::num::NonZero; use core::ptr; use core::ptr::*; @@ -1045,3 +1045,42 @@ fn test_ptr_default() { let default = PtrMutDefaultTest::default(); assert!(default.ptr.is_null()); } + +#[test] +fn test_const_drop_in_place() { + const COUNTER: usize = { + let mut counter = 0; + let counter_ptr = &raw mut counter; + + // only exists to make `Drop` indirect impl + #[allow(dead_code)] + struct Test(Dropped); + + struct Dropped(*mut usize); + impl const Drop for Dropped { + fn drop(&mut self) { + unsafe { + *self.0 += 1; + } + } + } + + let mut one = ManuallyDrop::new(Test(Dropped(counter_ptr))); + let mut two = ManuallyDrop::new(Test(Dropped(counter_ptr))); + let mut three = ManuallyDrop::new(Test(Dropped(counter_ptr))); + assert!(counter == 0); + unsafe { + ManuallyDrop::drop(&mut one); + } + assert!(counter == 1); + unsafe { + ManuallyDrop::drop(&mut two); + } + assert!(counter == 2); + unsafe { + ManuallyDrop::drop(&mut three); + } + counter + }; + assert_eq!(COUNTER, 3); +} diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 779b07ce240a6..d4108a57acaf0 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -33,7 +33,7 @@ miniz_oxide = { version = "0.8.0", optional = true, default-features = false } addr2line = { version = "0.25.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.172", default-features = false, features = [ +libc = { version = "0.2.177", default-features = false, features = [ 'rustc-dep-of-std', ], public = true } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 46eb120d65de3..60c8f53a0cc40 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -65,7 +65,7 @@ //! So for example there is a [page for the primitive type //! `char`](primitive::char) that lists all the methods that can be called on //! characters (very useful), and there is a [page for the module -//! `std::char`] that documents iterator and error types created by these methods +//! `std::char`](crate::char) that documents iterator and error types created by these methods //! (rarely useful). //! //! Note the documentation for the primitives [`str`] and [`[T]`][prim@slice] (also @@ -180,9 +180,6 @@ //! //! //! [I/O]: io -//! [`MIN`]: i32::MIN -//! [`MAX`]: i32::MAX -//! [page for the module `std::i32`]: crate::i32 //! [TCP]: net::TcpStream //! [The Rust Prelude]: prelude //! [UDP]: net::UdpSocket diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs index efa6f8d776559..b6c3ba4136f2a 100644 --- a/library/std/src/sys/pal/unix/sync/condvar.rs +++ b/library/std/src/sys/pal/unix/sync/condvar.rs @@ -1,11 +1,6 @@ use super::Mutex; use crate::cell::UnsafeCell; use crate::pin::Pin; -#[cfg(not(target_os = "nto"))] -use crate::sys::pal::time::TIMESPEC_MAX; -#[cfg(target_os = "nto")] -use crate::sys::pal::time::TIMESPEC_MAX_CAPPED; -use crate::sys::pal::time::Timespec; use crate::time::Duration; pub struct Condvar { @@ -47,27 +42,29 @@ impl Condvar { let r = unsafe { libc::pthread_cond_wait(self.raw(), mutex.raw()) }; debug_assert_eq!(r, 0); } +} +#[cfg(not(target_vendor = "apple"))] +impl Condvar { /// # Safety /// * `init` must have been called on this instance. /// * `mutex` must be locked by the current thread. /// * This condition variable may only be used with the same mutex. pub unsafe fn wait_timeout(&self, mutex: Pin<&Mutex>, dur: Duration) -> bool { + #[cfg(not(target_os = "nto"))] + use crate::sys::pal::time::TIMESPEC_MAX; + #[cfg(target_os = "nto")] + use crate::sys::pal::time::TIMESPEC_MAX_CAPPED; + use crate::sys::pal::time::Timespec; + let mutex = mutex.raw(); - // OSX implementation of `pthread_cond_timedwait` is buggy - // with super long durations. When duration is greater than - // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` - // in macOS Sierra returns error 316. - // - // This program demonstrates the issue: - // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c - // - // To work around this issue, the timeout is clamped to 1000 years. - // - // Cygwin implementation is based on NT API and a super large timeout - // makes the syscall block forever. - #[cfg(any(target_vendor = "apple", target_os = "cygwin"))] + // Cygwin's implementation is based on the NT API, which measures time + // in units of 100 ns. Unfortunately, Cygwin does not properly guard + // against overflow when converting the time, hence we clamp the interval + // to 1000 years, which will only become a problem in around 27000 years, + // when the next rollover is less than 1000 years away... + #[cfg(target_os = "cygwin")] let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400)); let timeout = Timespec::now(Self::CLOCK).checked_add_duration(&dur); @@ -84,6 +81,57 @@ impl Condvar { } } +// Apple platforms (since macOS version 10.4 and iOS version 2.0) have +// `pthread_cond_timedwait_relative_np`, a non-standard extension that +// measures timeouts based on the monotonic clock and is thus resilient +// against wall-clock changes. +#[cfg(target_vendor = "apple")] +impl Condvar { + /// # Safety + /// * `init` must have been called on this instance. + /// * `mutex` must be locked by the current thread. + /// * This condition variable may only be used with the same mutex. + pub unsafe fn wait_timeout(&self, mutex: Pin<&Mutex>, dur: Duration) -> bool { + let mutex = mutex.raw(); + + // The macOS implementation of `pthread_cond_timedwait` internally + // converts the timeout passed to `pthread_cond_timedwait_relative_np` + // to nanoseconds. Unfortunately, the "psynch" variant of condvars does + // not guard against overflow during the conversion[^1], which means + // that `pthread_cond_timedwait_relative_np` will return `ETIMEDOUT` + // much earlier than expected if the relative timeout is longer than + // `u64::MAX` nanoseconds. + // + // This can be observed even on newer platforms (by setting the environment + // variable PTHREAD_MUTEX_USE_ULOCK to a value other than "1") by calling e.g. + // ``` + // condvar.wait_timeout(..., Duration::from_secs(u64::MAX.div_ceil(1_000_000_000)); + // ``` + // (see #37440, especially + // https://github.com/rust-lang/rust/issues/37440#issuecomment-3285958326). + // + // To work around this issue, always clamp the timeout to u64::MAX nanoseconds, + // even if the "ulock" variant is used (which does guard against overflow). + // + // [^1]: https://github.com/apple-oss-distributions/libpthread/blob/1ebf56b3a702df53213c2996e5e128a535d2577e/kern/kern_synch.c#L1269 + const MAX_DURATION: Duration = Duration::from_nanos(u64::MAX); + + let (dur, clamped) = if dur <= MAX_DURATION { (dur, false) } else { (MAX_DURATION, true) }; + + let timeout = libc::timespec { + // This cannot overflow because of the clamping above. + tv_sec: dur.as_secs() as i64, + tv_nsec: dur.subsec_nanos() as i64, + }; + + let r = unsafe { libc::pthread_cond_timedwait_relative_np(self.raw(), mutex, &timeout) }; + assert!(r == libc::ETIMEDOUT || r == 0); + // Report clamping as a spurious wakeup. Who knows, maybe some + // interstellar space probe will rely on this ;-). + r == 0 || clamped + } +} + #[cfg(not(any( target_os = "android", target_vendor = "apple", @@ -125,10 +173,23 @@ impl Condvar { } } +#[cfg(target_vendor = "apple")] +impl Condvar { + // `pthread_cond_timedwait_relative_np` measures the timeout + // based on the monotonic clock. + pub const PRECISE_TIMEOUT: bool = true; + + /// # Safety + /// May only be called once per instance of `Self`. + pub unsafe fn init(self: Pin<&mut Self>) { + // `PTHREAD_COND_INITIALIZER` is fully supported and we don't need to + // change clocks, so there's nothing to do here. + } +} + // `pthread_condattr_setclock` is unfortunately not supported on these platforms. #[cfg(any( target_os = "android", - target_vendor = "apple", target_os = "espidf", target_os = "horizon", target_os = "l4re", diff --git a/library/std/tests/sync/condvar.rs b/library/std/tests/sync/condvar.rs index 1b1c33efad58e..2a525f9b5e948 100644 --- a/library/std/tests/sync/condvar.rs +++ b/library/std/tests/sync/condvar.rs @@ -267,3 +267,35 @@ nonpoison_and_poison_unwrap_test!( } } ); + +// Some platforms internally cast the timeout duration into nanoseconds. +// If they fail to consider overflow during the conversion (I'm looking +// at you, macOS), `wait_timeout` will return immediately and indicate a +// timeout for durations that are slightly longer than u64::MAX nanoseconds. +// `std` should guard against this by clamping the timeout. +// See #37440 for context. +nonpoison_and_poison_unwrap_test!( + name: timeout_nanoseconds, + test_body: { + use locks::Mutex; + use locks::Condvar; + + let sent = Mutex::new(false); + let cond = Condvar::new(); + + thread::scope(|s| { + s.spawn(|| { + thread::sleep(Duration::from_secs(2)); + maybe_unwrap(sent.set(true)); + cond.notify_all(); + }); + + let guard = maybe_unwrap(sent.lock()); + // If there is internal overflow, this call will return almost + // immediately, before the other thread has reached the `notify_all` + let (guard, res) = maybe_unwrap(cond.wait_timeout(guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000)))); + assert!(!res.timed_out()); + assert!(*guard); + }) + } +); diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 55906f4eb9548..d04ef5eac541b 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -815,7 +815,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_cond_timedwait" => { let [cond, mutex, abstime] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - this.pthread_cond_timedwait(cond, mutex, abstime, dest)?; + this.pthread_cond_timedwait(cond, mutex, abstime, dest, /* macos_relative_np */ false)?; } "pthread_cond_destroy" => { let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 297d903c6ba4e..0754eb45a2d29 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -307,6 +307,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.os_unfair_lock_assert_not_owner(lock_op)?; } + "pthread_cond_timedwait_relative_np" => { + let [cond, mutex, reltime] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.pthread_cond_timedwait(cond, mutex, reltime, dest, /* macos_relative_np */ true)?; + } + _ => return interp_ok(EmulateItemResult::NotSupported), }; diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index cefd8b81ac180..a712279d57628 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -834,8 +834,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, cond_op: &OpTy<'tcx>, mutex_op: &OpTy<'tcx>, - abstime_op: &OpTy<'tcx>, + timeout_op: &OpTy<'tcx>, dest: &MPlaceTy<'tcx>, + macos_relative_np: bool, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -844,7 +845,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Extract the timeout. let duration = match this - .read_timespec(&this.deref_pointer_as(abstime_op, this.libc_ty_layout("timespec"))?)? + .read_timespec(&this.deref_pointer_as(timeout_op, this.libc_ty_layout("timespec"))?)? { Some(duration) => duration, None => { @@ -853,14 +854,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(()); } }; - if data.clock == TimeoutClock::RealTime { - this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; - } + + let (clock, anchor) = if macos_relative_np { + // `pthread_cond_timedwait_relative_np` always measures time against the + // monotonic clock, regardless of the condvar clock. + (TimeoutClock::Monotonic, TimeoutAnchor::Relative) + } else { + if data.clock == TimeoutClock::RealTime { + this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; + } + + (data.clock, TimeoutAnchor::Absolute) + }; this.condvar_wait( data.condvar_ref, mutex_ref, - Some((data.clock, TimeoutAnchor::Absolute, duration)), + Some((clock, anchor, duration)), Scalar::from_i32(0), this.eval_libc("ETIMEDOUT"), // retval_timeout dest.clone(), diff --git a/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr b/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr index ec578db2d0a8e..e586d3f03994f 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr @@ -1,8 +1,10 @@ error: Undefined Behavior: trying to retag from for Unique permission at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location --> RUSTLIB/core/src/ptr/mod.rs:LL:CC | -LL | pub unsafe fn drop_in_place(to_drop: *mut T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this error occurs as part of retag at ALLOC[0x0..0x1] +LL | / pub const unsafe fn drop_in_place(to_drop: *mut T) +LL | | where +LL | | T: [const] Destruct, + | |________________________^ this error occurs as part of retag at ALLOC[0x0..0x1] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr b/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr index 72aeb0fdf8e49..ea144f206643a 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr +++ b/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr @@ -1,8 +1,10 @@ error: Undefined Behavior: constructing invalid value: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN) --> RUSTLIB/core/src/ptr/mod.rs:LL:CC | -LL | pub unsafe fn drop_in_place(to_drop: *mut T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | / pub const unsafe fn drop_in_place(to_drop: *mut T) +LL | | where +LL | | T: [const] Destruct, + | |________________________^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/pass-dep/libc/pthread_cond_timedwait_relative_np_apple.rs b/src/tools/miri/tests/pass-dep/libc/pthread_cond_timedwait_relative_np_apple.rs new file mode 100644 index 0000000000000..bcf06211b8c41 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/pthread_cond_timedwait_relative_np_apple.rs @@ -0,0 +1,41 @@ +//@only-target: apple # `pthread_cond_timedwait_relative_np` is a non-standard extension + +use std::time::Instant; + +// FIXME: remove once this is in libc. +mod libc { + pub use ::libc::*; + unsafe extern "C" { + pub unsafe fn pthread_cond_timedwait_relative_np( + cond: *mut libc::pthread_cond_t, + lock: *mut libc::pthread_mutex_t, + timeout: *const libc::timespec, + ) -> libc::c_int; + } +} + +fn main() { + unsafe { + let mut mutex: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER; + let mut cond: libc::pthread_cond_t = libc::PTHREAD_COND_INITIALIZER; + + // Wait for 100 ms. + let timeout = libc::timespec { tv_sec: 0, tv_nsec: 100_000_000 }; + + assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); + + let current_time = Instant::now(); + assert_eq!( + libc::pthread_cond_timedwait_relative_np(&mut cond, &mut mutex, &timeout), + libc::ETIMEDOUT + ); + let elapsed_time = current_time.elapsed().as_millis(); + // This is actually deterministic (since isolation remains enabled), + // but can change slightly with Rust updates. + assert!(90 <= elapsed_time && elapsed_time <= 110); + + assert_eq!(libc::pthread_mutex_unlock(&mut mutex), 0); + assert_eq!(libc::pthread_mutex_destroy(&mut mutex), 0); + assert_eq!(libc::pthread_cond_destroy(&mut cond), 0); + } +} diff --git a/tests/ui/macros/macro-expansion-empty-span-147408.rs b/tests/ui/macros/macro-expansion-empty-span-147408.rs new file mode 100644 index 0000000000000..ef3f7b40abbf1 --- /dev/null +++ b/tests/ui/macros/macro-expansion-empty-span-147408.rs @@ -0,0 +1,23 @@ +//@ check-pass + +fn main() { + macro_rules! mac { + (iter $e:expr) => { + $e.iter() + }; + (into_iter $e:expr) => { + $e.into_iter() //~ WARN this method call resolves to + //~^ WARN this changes meaning in Rust 2021 + }; + (next $e:expr) => { + $e.iter().next() + }; + } + + for _ in dbg!([1, 2]).iter() {} + for _ in dbg!([1, 2]).into_iter() {} //~ WARN this method call resolves to + //~^ WARN this changes meaning in Rust 2021 + for _ in mac!(iter [1, 2]) {} + for _ in mac!(into_iter [1, 2]) {} + for _ in mac!(next [1, 2]) {} //~ WARN for loop over an +} diff --git a/tests/ui/macros/macro-expansion-empty-span-147408.stderr b/tests/ui/macros/macro-expansion-empty-span-147408.stderr new file mode 100644 index 0000000000000..420252bc6888e --- /dev/null +++ b/tests/ui/macros/macro-expansion-empty-span-147408.stderr @@ -0,0 +1,58 @@ + WARN rustc_errors::emitter Invalid span $SRC_DIR/std/src/macros.rs:LL:COL (#11), error=SourceNotAvailable { filename: Real(Remapped { local_path: None, virtual_name: "$SRC_DIR/std/src/macros.rs" }) } +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 + --> $DIR/macro-expansion-empty-span-147408.rs:18:27 + | +LL | for _ in dbg!([1, 2]).into_iter() {} + | ^^^^^^^^^ + | + = warning: this changes meaning in Rust 2021 + = note: for more information, see + = note: `#[warn(array_into_iter)]` (part of `#[warn(rust_2021_compatibility)]`) on by default +help: use `.iter()` instead of `.into_iter()` to avoid ambiguity + | +LL - for _ in dbg!([1, 2]).into_iter() {} +LL + for _ in dbg!([1, 2]).iter() {} + | + +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 + --> $DIR/macro-expansion-empty-span-147408.rs:9:16 + | +LL | $e.into_iter() + | ^^^^^^^^^ +... +LL | for _ in mac!(into_iter [1, 2]) {} + | ---------------------- in this macro invocation + | + = warning: this changes meaning in Rust 2021 + = note: for more information, see + = note: this warning originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use `.iter()` instead of `.into_iter()` to avoid ambiguity + | +LL - $e.into_iter() +LL + $e.iter() + | +help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value + | +LL | IntoIterator::into_iter($e.into_iter()) + | ++++++++++++++++++++++++ + + +warning: for loop over an `Option`. This is more readably written as an `if let` statement + --> $DIR/macro-expansion-empty-span-147408.rs:22:14 + | +LL | for _ in mac!(next [1, 2]) {} + | ^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(for_loops_over_fallibles)]` on by default +help: to iterate over `$e.iter()` remove the call to `next` + | +LL - $e.iter().next() +LL + .by_ref().next() + | +help: consider using `if let` to clear intent + | +LL - for _ in mac!(next [1, 2]) {} +LL + if let Some(_) = mac!(next [1, 2]) {} + | + +warning: 3 warnings emitted +