Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e163707
Constify `DropGuard` methods
nxsaken Nov 9, 2025
e0c09b5
fix va_list test by adding a llvmir signext check
fneddy Dec 8, 2025
34392a9
Fix(alloc): Correctly handle ZST alignment for IntoIter::nth_back
DrAsu33 Nov 29, 2025
4e51a8d
tidy: Detect outdated workspaces in workspace list
jamie-osec Nov 28, 2025
ac5c70a
time: Implement SystemTime::{MIN, MAX}
cvengler Nov 11, 2025
29f688a
Update to mdbook 0.5
ehuss Dec 11, 2025
1b9b4f4
time: Test and document time precision edge-case
cvengler Dec 13, 2025
0ecf91a
Use an explicit receiver in `DropGuard::dismiss`
nxsaken Dec 13, 2025
d80348b
time: Fix Windows' `SystemTime::checked_sub`
cvengler Dec 13, 2025
d484f93
Fix typo in armv7a-vex-v5 documentation
elijah629 Dec 13, 2025
98e1028
Enable to ping LoongArch group via triagebot
heiher Dec 14, 2025
01e40d6
Rollup merge of #148755 - nxsaken:const_drop_guard, r=dtolnay
ChrisDenton Dec 14, 2025
46814a9
Rollup merge of #148825 - cvengler:time_systemtime_limits, r=ChrisDenton
ChrisDenton Dec 14, 2025
735c45e
Rollup merge of #149272 - DrAsu33:fix-vec-iter-zst-alignment-148682, …
ChrisDenton Dec 14, 2025
bd74709
Rollup merge of #149417 - clubby789:stale-workspace-list, r=Mark-Simu…
ChrisDenton Dec 14, 2025
2f06db1
Rollup merge of #149773 - fneddy:fix_test_va_list_signext, r=Mark-Sim…
ChrisDenton Dec 14, 2025
ba1de8a
Rollup merge of #149894 - ehuss:mdbook-0.5, r=Mark-Simulacrum
ChrisDenton Dec 14, 2025
5a450d0
Rollup merge of #149955 - elijah629:patch-1, r=Noratrieb
ChrisDenton Dec 14, 2025
fbd259d
Rollup merge of #149972 - heiher:ping-loongarch, r=jieyouxu
ChrisDenton Dec 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion library/alloc/src/vec/into_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,12 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
// SAFETY: same as for advance_by()
self.end = unsafe { self.end.sub(step_size) };
}
let to_drop = ptr::slice_from_raw_parts_mut(self.end as *mut T, step_size);
let to_drop = if T::IS_ZST {
// ZST may cause unalignment
ptr::slice_from_raw_parts_mut(ptr::NonNull::<T>::dangling().as_ptr(), step_size)
} else {
ptr::slice_from_raw_parts_mut(self.end as *mut T, step_size)
};
// SAFETY: same as for advance_by()
unsafe {
ptr::drop_in_place(to_drop);
Expand Down
32 changes: 32 additions & 0 deletions library/alloctests/tests/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2717,3 +2717,35 @@ fn vec_null_ptr_roundtrip() {
let new = roundtripped.with_addr(ptr.addr());
unsafe { new.read() };
}

// Regression test for Undefined Behavior (UB) caused by IntoIter::nth_back (#148682)
// when dealing with high-aligned Zero-Sized Types (ZSTs).
use std::collections::{BTreeMap, BinaryHeap, HashMap, LinkedList, VecDeque};
#[test]
fn zst_collections_iter_nth_back_regression() {
#[repr(align(8))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
struct Thing;
let v = vec![Thing, Thing];
let _ = v.into_iter().nth_back(1);
let mut d = VecDeque::new();
d.push_back(Thing);
d.push_back(Thing);
let _ = d.into_iter().nth_back(1);
let mut map = BTreeMap::new();
map.insert(0, Thing);
map.insert(1, Thing);
let _ = map.into_values().nth_back(0);
let mut hash_map = HashMap::new();
hash_map.insert(1, Thing);
hash_map.insert(2, Thing);
let _ = hash_map.into_values().nth(1);
let mut heap = BinaryHeap::new();
heap.push(Thing);
heap.push(Thing);
let _ = heap.into_iter().nth_back(1);
let mut list = LinkedList::new();
list.push_back(Thing);
list.push_back(Thing);
let _ = list.into_iter().nth_back(1);
}
26 changes: 17 additions & 9 deletions library/core/src/mem/drop_guard.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::fmt::{self, Debug};
use crate::marker::Destruct;
use crate::mem::ManuallyDrop;
use crate::ops::{Deref, DerefMut};

Expand Down Expand Up @@ -78,32 +79,37 @@ where
///
/// let value = String::from("Nori likes chicken");
/// let guard = DropGuard::new(value, |s| println!("{s}"));
/// assert_eq!(guard.dismiss(), "Nori likes chicken");
/// assert_eq!(DropGuard::dismiss(guard), "Nori likes chicken");
/// ```
#[unstable(feature = "drop_guard", issue = "144426")]
#[rustc_const_unstable(feature = "const_drop_guard", issue = "none")]
#[inline]
pub fn dismiss(self) -> T {
pub const fn dismiss(guard: Self) -> T
where
F: [const] Destruct,
{
// First we ensure that dropping the guard will not trigger
// its destructor
let mut this = ManuallyDrop::new(self);
let mut guard = ManuallyDrop::new(guard);

// Next we manually read the stored value from the guard.
//
// SAFETY: this is safe because we've taken ownership of the guard.
let value = unsafe { ManuallyDrop::take(&mut this.inner) };
let value = unsafe { ManuallyDrop::take(&mut guard.inner) };

// Finally we drop the stored closure. We do this *after* having read
// the value, so that even if the closure's `drop` function panics,
// unwinding still tries to drop the value.
//
// SAFETY: this is safe because we've taken ownership of the guard.
unsafe { ManuallyDrop::drop(&mut this.f) };
unsafe { ManuallyDrop::drop(&mut guard.f) };
value
}
}

#[unstable(feature = "drop_guard", issue = "144426")]
impl<T, F> Deref for DropGuard<T, F>
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<T, F> const Deref for DropGuard<T, F>
where
F: FnOnce(T),
{
Expand All @@ -115,7 +121,8 @@ where
}

#[unstable(feature = "drop_guard", issue = "144426")]
impl<T, F> DerefMut for DropGuard<T, F>
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<T, F> const DerefMut for DropGuard<T, F>
where
F: FnOnce(T),
{
Expand All @@ -125,9 +132,10 @@ where
}

#[unstable(feature = "drop_guard", issue = "144426")]
impl<T, F> Drop for DropGuard<T, F>
#[rustc_const_unstable(feature = "const_drop_guard", issue = "none")]
impl<T, F> const Drop for DropGuard<T, F>
where
F: FnOnce(T),
F: [const] FnOnce(T),
{
fn drop(&mut self) {
// SAFETY: `DropGuard` is in the process of being dropped.
Expand Down
4 changes: 2 additions & 2 deletions library/coretests/tests/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ fn drop_guard_into_inner() {
let dropped = Cell::new(false);
let value = DropGuard::new(42, |_| dropped.set(true));
let guard = DropGuard::new(value, |_| dropped.set(true));
let inner = guard.dismiss();
let inner = DropGuard::dismiss(guard);
assert_eq!(dropped.get(), false);
assert_eq!(*inner, 42);
}
Expand All @@ -837,7 +837,7 @@ fn drop_guard_always_drops_value_if_closure_drop_unwinds() {
// run the destructor of the value we passed, which we validate.
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let guard = DropGuard::new(value_with_tracked_destruction, closure_that_panics_on_drop);
guard.dismiss();
DropGuard::dismiss(guard);
}));
assert!(value_was_dropped);
}
8 changes: 8 additions & 0 deletions library/std/src/sys/pal/hermit/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ struct Timespec {
}

impl Timespec {
const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1);

const MIN: Timespec = Self::new(i64::MIN, 0);

const fn zero() -> Timespec {
Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } }
}
Expand Down Expand Up @@ -209,6 +213,10 @@ pub struct SystemTime(Timespec);
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());

impl SystemTime {
pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };

pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };

pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime {
SystemTime(Timespec::new(tv_sec, tv_nsec))
}
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/pal/sgx/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ impl Instant {
}

impl SystemTime {
pub const MAX: SystemTime = SystemTime(Duration::MAX);

pub const MIN: SystemTime = SystemTime(Duration::ZERO);

pub fn now() -> SystemTime {
SystemTime(usercalls::insecure_time())
}
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/pal/solid/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ pub struct SystemTime(abi::time_t);
pub const UNIX_EPOCH: SystemTime = SystemTime(0);

impl SystemTime {
pub const MAX: SystemTime = SystemTime(abi::time_t::MAX);

pub const MIN: SystemTime = SystemTime(abi::time_t::MIN);

pub fn now() -> SystemTime {
let rtc = unsafe {
let mut out = MaybeUninit::zeroed();
Expand Down
17 changes: 17 additions & 0 deletions library/std/src/sys/pal/uefi/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ impl Instant {
}

impl SystemTime {
pub const MAX: SystemTime = MAX_UEFI_TIME;

pub const MIN: SystemTime = SystemTime::from_uefi(r_efi::efi::Time {
year: 1900,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
nanosecond: 0,
timezone: -1440,
daylight: 0,
pad1: 0,
pad2: 0,
})
.unwrap();

pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Option<Self> {
match system_time_internal::from_uefi(&t) {
Some(x) => Some(Self(x)),
Expand Down
11 changes: 11 additions & 0 deletions library/std/src/sys/pal/unix/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ pub(crate) struct Timespec {
}

impl SystemTime {
pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };

pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };

#[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))]
pub fn new(tv_sec: i64, tv_nsec: i64) -> Result<SystemTime, io::Error> {
Ok(SystemTime { t: Timespec::new(tv_sec, tv_nsec)? })
Expand Down Expand Up @@ -62,6 +66,13 @@ impl fmt::Debug for SystemTime {
}

impl Timespec {
const MAX: Timespec = unsafe { Self::new_unchecked(i64::MAX, 1_000_000_000 - 1) };

// As described below, on Apple OS, dates before epoch are represented differently.
// This is not an issue here however, because we are using tv_sec = i64::MIN,
// which will cause the compatibility wrapper to not be executed at all.
const MIN: Timespec = unsafe { Self::new_unchecked(i64::MIN, 0) };

const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec {
Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } }
}
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/pal/unsupported/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ impl Instant {
}

impl SystemTime {
pub const MAX: SystemTime = SystemTime(Duration::MAX);

pub const MIN: SystemTime = SystemTime(Duration::ZERO);

pub fn now() -> SystemTime {
panic!("time not implemented on this platform")
}
Expand Down
19 changes: 17 additions & 2 deletions library/std/src/sys/pal/windows/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ impl Instant {
}

impl SystemTime {
pub const MAX: SystemTime = SystemTime {
t: c::FILETIME {
dwLowDateTime: (i64::MAX & 0xFFFFFFFF) as u32,
dwHighDateTime: (i64::MAX >> 32) as u32,
},
};

pub const MIN: SystemTime =
SystemTime { t: c::FILETIME { dwLowDateTime: 0, dwHighDateTime: 0 } };

pub fn now() -> SystemTime {
unsafe {
let mut t: SystemTime = mem::zeroed();
Expand Down Expand Up @@ -101,8 +111,13 @@ impl SystemTime {
}

pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
let intervals = self.intervals().checked_sub(checked_dur2intervals(other)?)?;
Some(SystemTime::from_intervals(intervals))
// Windows does not support times before 1601, hence why we don't
// support negatives. In order to tackle this, we try to convert the
// resulting value into an u64, which should obviously fail in the case
// that the value is below zero.
let intervals: u64 =
self.intervals().checked_sub(checked_dur2intervals(other)?)?.try_into().ok()?;
Some(SystemTime::from_intervals(intervals as i64))
}
}

Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/pal/xous/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ impl Instant {
}

impl SystemTime {
pub const MAX: SystemTime = SystemTime(Duration::MAX);

pub const MIN: SystemTime = SystemTime(Duration::ZERO);

pub fn now() -> SystemTime {
let result = blocking_scalar(systime_server(), GetUtcTimeMs.into())
.expect("failed to request utc time in ms");
Expand Down
83 changes: 83 additions & 0 deletions library/std/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,83 @@ impl SystemTime {
#[stable(feature = "assoc_unix_epoch", since = "1.28.0")]
pub const UNIX_EPOCH: SystemTime = UNIX_EPOCH;

/// Represents the maximum value representable by [`SystemTime`] on this platform.
///
/// This value differs a lot between platforms, but it is always the case
/// that any positive addition of a [`Duration`], whose value is greater
/// than or equal to the time precision of the operating system, to
/// [`SystemTime::MAX`] will fail.
///
/// # Examples
///
/// ```no_run
/// #![feature(time_systemtime_limits)]
/// use std::time::{Duration, SystemTime};
///
/// // Adding zero will change nothing.
/// assert_eq!(SystemTime::MAX.checked_add(Duration::ZERO), Some(SystemTime::MAX));
///
/// // But adding just one second will already fail ...
/// //
/// // Keep in mind that this in fact may succeed, if the Duration is
/// // smaller than the time precision of the operating system, which
/// // happens to be 1ns on most operating systems, with Windows being the
/// // notable exception by using 100ns, hence why this example uses 1s.
/// assert_eq!(SystemTime::MAX.checked_add(Duration::new(1, 0)), None);
///
/// // Utilize this for saturating arithmetic to improve error handling.
/// // In this case, we will use a certificate with a timestamp in the
/// // future as a practical example.
/// let configured_offset = Duration::from_secs(60 * 60 * 24);
/// let valid_after =
/// SystemTime::now()
/// .checked_add(configured_offset)
/// .unwrap_or(SystemTime::MAX);
/// ```
#[unstable(feature = "time_systemtime_limits", issue = "149067")]
pub const MAX: SystemTime = SystemTime(time::SystemTime::MAX);

/// Represents the minimum value representable by [`SystemTime`] on this platform.
///
/// This value differs a lot between platforms, but it is always the case
/// that any positive subtraction of a [`Duration`] from, whose value is
/// greater than or equal to the time precision of the operating system, to
/// [`SystemTime::MIN`] will fail.
///
/// Depending on the platform, this may be either less than or equal to
/// [`SystemTime::UNIX_EPOCH`], depending on whether the operating system
/// supports the representation of timestamps before the Unix epoch or not.
/// However, it is always guaranteed that a [`SystemTime::UNIX_EPOCH`] fits
/// between a [`SystemTime::MIN`] and [`SystemTime::MAX`].
///
/// # Examples
///
/// ```
/// #![feature(time_systemtime_limits)]
/// use std::time::{Duration, SystemTime};
///
/// // Subtracting zero will change nothing.
/// assert_eq!(SystemTime::MIN.checked_sub(Duration::ZERO), Some(SystemTime::MIN));
///
/// // But subtracting just one second will already fail.
/// //
/// // Keep in mind that this in fact may succeed, if the Duration is
/// // smaller than the time precision of the operating system, which
/// // happens to be 1ns on most operating systems, with Windows being the
/// // notable exception by using 100ns, hence why this example uses 1s.
/// assert_eq!(SystemTime::MIN.checked_sub(Duration::new(1, 0)), None);
///
/// // Utilize this for saturating arithmetic to improve error handling.
/// // In this case, we will use a cache expiry as a practical example.
/// let configured_expiry = Duration::from_secs(60 * 3);
/// let expiry_threshold =
/// SystemTime::now()
/// .checked_sub(configured_expiry)
/// .unwrap_or(SystemTime::MIN);
/// ```
#[unstable(feature = "time_systemtime_limits", issue = "149067")]
pub const MIN: SystemTime = SystemTime(time::SystemTime::MIN);

/// Returns the system time corresponding to "now".
///
/// # Examples
Expand Down Expand Up @@ -588,6 +665,9 @@ impl SystemTime {
/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
/// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
///
/// In the case that the `duration` is smaller than the time precision of the operating
/// system, `Some(self)` will be returned.
#[stable(feature = "time_checked_add", since = "1.34.0")]
pub fn checked_add(&self, duration: Duration) -> Option<SystemTime> {
self.0.checked_add_duration(&duration).map(SystemTime)
Expand All @@ -596,6 +676,9 @@ impl SystemTime {
/// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
/// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
///
/// In the case that the `duration` is smaller than the time precision of the operating
/// system, `Some(self)` will be returned.
#[stable(feature = "time_checked_add", since = "1.34.0")]
pub fn checked_sub(&self, duration: Duration) -> Option<SystemTime> {
self.0.checked_sub_duration(&duration).map(SystemTime)
Expand Down
Loading
Loading