From aa537824c40437d11d32c66fe16d4764b072c094 Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 24 Sep 2025 00:50:36 +0200 Subject: [PATCH 1/9] core: simplify `CStr::default()` Just use a `CStr`-literal... --- library/core/src/ffi/c_str.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index d0b53e3a237f6..09d9b160700ca 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -179,9 +179,7 @@ impl fmt::Debug for CStr { impl Default for &CStr { #[inline] fn default() -> Self { - const SLICE: &[c_char] = &[0]; - // SAFETY: `SLICE` is indeed pointing to a valid nul-terminated string. - unsafe { CStr::from_ptr(SLICE.as_ptr()) } + c"" } } From 32d24f9efa81a1c467da21c3ae2d93a6b00a9f5b Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 24 Sep 2025 13:02:27 +0200 Subject: [PATCH 2/9] allow bound regions in writeback --- compiler/rustc_hir_typeck/src/writeback.rs | 6 +++-- tests/crashes/117808.rs | 27 ------------------- .../writeback-predicate-bound-region.rs | 14 ++++++++++ 3 files changed, 18 insertions(+), 29 deletions(-) delete mode 100644 tests/crashes/117808.rs create mode 100644 tests/ui/traits/next-solver/writeback-predicate-bound-region.rs diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 6192420898f6e..d01eeb9a4b691 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -1003,8 +1003,10 @@ impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - debug_assert!(!r.is_bound(), "Should not be resolving bound region."); - self.fcx.tcx.lifetimes.re_erased + match r.kind() { + ty::ReBound(..) => r, + _ => self.fcx.tcx.lifetimes.re_erased, + } } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { diff --git a/tests/crashes/117808.rs b/tests/crashes/117808.rs deleted file mode 100644 index 2c727986dd07f..0000000000000 --- a/tests/crashes/117808.rs +++ /dev/null @@ -1,27 +0,0 @@ -//@ known-bug: #117808 -//@ edition:2021 -//@ needs-rustc-debug-assertions - -use std::future::Future; - -fn hrc AsyncClosure<'a, (), R>>(f: F) -> F { - f -} - -fn main() { - hrc(|x| async {}); -} - -trait AsyncClosure<'a, I, R> -where - I: 'a, -{ -} - -impl<'a, I, R, Fut, F> AsyncClosure<'a, I, R> for F -where - I: 'a, - F: Fn(&'a I) -> Fut, - Fut: Future + Send + 'a, -{ -} diff --git a/tests/ui/traits/next-solver/writeback-predicate-bound-region.rs b/tests/ui/traits/next-solver/writeback-predicate-bound-region.rs new file mode 100644 index 0000000000000..a7ed5dbcf086d --- /dev/null +++ b/tests/ui/traits/next-solver/writeback-predicate-bound-region.rs @@ -0,0 +1,14 @@ +//@ edition: 2024 +//@ check-pass +//@ compile-flags: -Znext-solver + +// This previously ICE'd during writeback when resolving +// the stalled coroutine predicate due to its bound lifetime. + +trait Trait<'a> {} +impl<'a, T: Send> Trait<'a> for T {} + +fn is_trait Trait<'a>>(_: T) {} +fn main() { + is_trait(async {}) +} From 2886ca496a8adff7424da8b19eeaffa9d392210c Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 24 Sep 2025 13:04:36 +0200 Subject: [PATCH 3/9] imrpove type_op failure ICE --- .../src/traits/query/type_op/custom.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 6ce68507d6521..a96cb738b81f0 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -100,9 +100,9 @@ where } else if let Err(guar) = infcx.tcx.check_potentially_region_dependent_goals(root_def_id) { Err(guar) } else { - Err(infcx - .dcx() - .delayed_bug(format!("errors selecting obligation during MIR typeck: {errors:?}"))) + Err(infcx.dcx().delayed_bug(format!( + "errors selecting obligation during MIR typeck: {name} {root_def_id:?} {errors:?}" + ))) } })?; From a875f7779e3c3b9023bff24b6beca08e7be0e0e8 Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 24 Sep 2025 14:13:34 +0200 Subject: [PATCH 4/9] alloc: simplify `Default` for `Box` and `Rc` --- library/alloc/src/ffi/c_str.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index b0c8c4b1ca4a7..3e78d680ea68a 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -970,17 +970,14 @@ impl Default for Rc { /// This may or may not share an allocation with other Rcs on the same thread. #[inline] fn default() -> Self { - let rc = Rc::<[u8]>::from(*b"\0"); - // `[u8]` has the same layout as `CStr`, and it is `NUL` terminated. - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) } + Rc::from(c"") } } #[stable(feature = "default_box_extra", since = "1.17.0")] impl Default for Box { fn default() -> Box { - let boxed: Box<[u8]> = Box::from([0]); - unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) } + Box::from(c"") } } From 30289353e8e04eff33a0e533b6b398c308b24025 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 22 Sep 2025 15:50:22 +0000 Subject: [PATCH 5/9] Improve derive suggestion of const param Make the suggestion not to remove the adt and use the name of the adt variant --- .../src/error_reporting/traits/fulfillment_errors.rs | 11 +++++++---- .../forbid-non-structural_match-types.stderr | 2 +- tests/ui/const-generics/issue-80471.stderr | 4 ++-- tests/ui/const-generics/issues/issue-97278.stderr | 4 ++-- .../ui/consts/refs_check_const_eq-issue-88384.stderr | 4 ++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 149f5e638b1ad..d485eb7266b28 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1305,8 +1305,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { if ty.is_structural_eq_shallow(self.tcx) { diag.span_suggestion( - span, - "add `#[derive(ConstParamTy)]` to the struct", + span.shrink_to_lo(), + format!("add `#[derive(ConstParamTy)]` to the {}", def.descr()), "#[derive(ConstParamTy)]\n", Applicability::MachineApplicable, ); @@ -1314,8 +1314,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // FIXME(adt_const_params): We should check there's not already an // overlapping `Eq`/`PartialEq` impl. diag.span_suggestion( - span, - "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct", + span.shrink_to_lo(), + format!( + "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the {}", + def.descr() + ), "#[derive(ConstParamTy, PartialEq, Eq)]\n", Applicability::MachineApplicable, ); diff --git a/tests/ui/const-generics/forbid-non-structural_match-types.stderr b/tests/ui/const-generics/forbid-non-structural_match-types.stderr index 8ef629329f103..94afded9469e8 100644 --- a/tests/ui/const-generics/forbid-non-structural_match-types.stderr +++ b/tests/ui/const-generics/forbid-non-structural_match-types.stderr @@ -6,8 +6,8 @@ LL | struct D; | help: add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct | -LL - struct C; LL + #[derive(ConstParamTy, PartialEq, Eq)] +LL | struct C; | error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/issue-80471.stderr b/tests/ui/const-generics/issue-80471.stderr index 8cf3d68e5d691..fff2eb53cf126 100644 --- a/tests/ui/const-generics/issue-80471.stderr +++ b/tests/ui/const-generics/issue-80471.stderr @@ -4,10 +4,10 @@ error[E0741]: `Nat` must implement `ConstParamTy` to be used as the type of a co LL | fn foo() {} | ^^^ | -help: add `#[derive(ConstParamTy)]` to the struct +help: add `#[derive(ConstParamTy)]` to the enum | -LL - enum Nat { LL + #[derive(ConstParamTy)] +LL | enum Nat { | error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/issues/issue-97278.stderr b/tests/ui/const-generics/issues/issue-97278.stderr index 4894ddb7b8db8..21a5fc94032c0 100644 --- a/tests/ui/const-generics/issues/issue-97278.stderr +++ b/tests/ui/const-generics/issues/issue-97278.stderr @@ -4,10 +4,10 @@ error[E0741]: `Bar` must implement `ConstParamTy` to be used as the type of a co LL | fn test() {} | ^^^ | -help: add `#[derive(ConstParamTy)]` to the struct +help: add `#[derive(ConstParamTy)]` to the enum | -LL - enum Bar { LL + #[derive(ConstParamTy)] +LL | enum Bar { | error: aborting due to 1 previous error diff --git a/tests/ui/consts/refs_check_const_eq-issue-88384.stderr b/tests/ui/consts/refs_check_const_eq-issue-88384.stderr index 65e4dc22c2887..62c5c52764115 100644 --- a/tests/ui/consts/refs_check_const_eq-issue-88384.stderr +++ b/tests/ui/consts/refs_check_const_eq-issue-88384.stderr @@ -15,8 +15,8 @@ LL | struct Foo; | help: add `#[derive(ConstParamTy)]` to the struct | -LL - struct CompileTimeSettings { LL + #[derive(ConstParamTy)] +LL | struct CompileTimeSettings { | error[E0741]: `CompileTimeSettings` must implement `ConstParamTy` to be used as the type of a const generic parameter @@ -27,8 +27,8 @@ LL | impl Foo { | help: add `#[derive(ConstParamTy)]` to the struct | -LL - struct CompileTimeSettings { LL + #[derive(ConstParamTy)] +LL | struct CompileTimeSettings { | error: aborting due to 2 previous errors; 1 warning emitted From 5a4e53603631475335bab3e1416f4aca61172094 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Wed, 24 Sep 2025 12:55:38 +0800 Subject: [PATCH 6/9] Fix infinite recursion in Path::eq with String --- library/std/src/path.rs | 12 ++++++------ library/std/tests/path.rs | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 70ba502d68421..88d8a4f21cac5 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2107,7 +2107,7 @@ impl PartialEq for PathBuf { impl cmp::PartialEq for PathBuf { #[inline] fn eq(&self, other: &str) -> bool { - Path::eq(self, other) + self.as_path() == other } } @@ -2115,7 +2115,7 @@ impl cmp::PartialEq for PathBuf { impl cmp::PartialEq for str { #[inline] fn eq(&self, other: &PathBuf) -> bool { - other == self + self == other.as_path() } } @@ -2123,7 +2123,7 @@ impl cmp::PartialEq for str { impl cmp::PartialEq for PathBuf { #[inline] fn eq(&self, other: &String) -> bool { - **self == **other + self.as_path() == other.as_str() } } @@ -2131,7 +2131,7 @@ impl cmp::PartialEq for PathBuf { impl cmp::PartialEq for String { #[inline] fn eq(&self, other: &PathBuf) -> bool { - other == self + self.as_str() == other.as_path() } } @@ -3426,7 +3426,7 @@ impl cmp::PartialEq for str { impl cmp::PartialEq for Path { #[inline] fn eq(&self, other: &String) -> bool { - self == &*other + self == other.as_str() } } @@ -3434,7 +3434,7 @@ impl cmp::PartialEq for Path { impl cmp::PartialEq for String { #[inline] fn eq(&self, other: &Path) -> bool { - other == self + self.as_str() == other } } diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index fa76c50597b05..837a14b808ff8 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -2528,7 +2528,17 @@ fn normalize_lexically() { } #[test] -/// See issue#146183 -fn compare_path_to_str() { - assert!(&PathBuf::from("x") == "x"); +/// See issue#146183 and issue#146940 +fn compare_path_like_to_str_like() { + let path_buf = PathBuf::from("x"); + let path = Path::new("x"); + let s = String::from("x"); + assert!(path == "x"); + assert!("x" == path); + assert!(path == &s); + assert!(&s == path); + assert!(&path_buf == "x"); + assert!("x" == &path_buf); + assert!(path_buf == s); + assert!(s == path_buf); } From 4e62715541925e1a62aebb3b8a0f633a674b808d Mon Sep 17 00:00:00 2001 From: tiif Date: Fri, 19 Sep 2025 07:07:49 +0000 Subject: [PATCH 7/9] Improve the pretty print of UnstableFeature clause --- compiler/rustc_middle/src/ty/print/pretty.rs | 3 +-- .../const-generics/adt_const_params/unsized_field-1.stderr | 6 +++--- .../unstable_impl_coherence.disabled.stderr | 2 +- .../unstable_impl_coherence.enabled.stderr | 2 +- .../unstable_impl_method_selection.stderr | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 1b7ef8de8454a..8f7c8170f7adf 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3177,8 +3177,7 @@ define_print! { write!(p, "` can be evaluated")?; } ty::ClauseKind::UnstableFeature(symbol) => { - write!(p, "unstable feature: ")?; - write!(p, "`{symbol}`")?; + write!(p, "feature({symbol}) is enabled")?; } } } diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr b/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr index a5ae5c726da86..134dbba0d63ab 100644 --- a/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr +++ b/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr @@ -7,7 +7,7 @@ LL | LL | struct A([u8]); | ---- this field does not implement `ConstParamTy_` | -note: the `ConstParamTy_` impl for `[u8]` requires that `unstable feature: `unsized_const_params`` +note: the `ConstParamTy_` impl for `[u8]` requires that `feature(unsized_const_params) is enabled` --> $DIR/unsized_field-1.rs:10:10 | LL | struct A([u8]); @@ -22,7 +22,7 @@ LL | LL | struct B(&'static [u8]); | ------------- this field does not implement `ConstParamTy_` | -note: the `ConstParamTy_` impl for `&'static [u8]` requires that `unstable feature: `unsized_const_params`` +note: the `ConstParamTy_` impl for `&'static [u8]` requires that `feature(unsized_const_params) is enabled` --> $DIR/unsized_field-1.rs:14:10 | LL | struct B(&'static [u8]); @@ -37,7 +37,7 @@ LL | LL | struct D(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); | ---------------------------------------------------------- this field does not implement `ConstParamTy_` | -note: the `ConstParamTy_` impl for `GenericNotUnsizedParam<&'static [u8]>` requires that `unstable feature: `unsized_const_params`` +note: the `ConstParamTy_` impl for `GenericNotUnsizedParam<&'static [u8]>` requires that `feature(unsized_const_params) is enabled` --> $DIR/unsized_field-1.rs:21:10 | LL | struct D(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); diff --git a/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr b/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr index c3147558b03f9..afef024e1b9c1 100644 --- a/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr +++ b/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr @@ -6,7 +6,7 @@ LL | impl aux::Trait for LocalTy {} | = note: conflicting implementation in crate `unstable_impl_coherence_aux`: - impl Trait for T - where unstable feature: `foo`; + where feature(foo) is enabled; error: aborting due to 1 previous error diff --git a/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr b/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr index c3147558b03f9..afef024e1b9c1 100644 --- a/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr +++ b/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr @@ -6,7 +6,7 @@ LL | impl aux::Trait for LocalTy {} | = note: conflicting implementation in crate `unstable_impl_coherence_aux`: - impl Trait for T - where unstable feature: `foo`; + where feature(foo) is enabled; error: aborting due to 1 previous error diff --git a/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr b/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr index c2bb10f043b2e..840af730154d9 100644 --- a/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr +++ b/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr @@ -7,7 +7,7 @@ LL | vec![].foo(); = note: multiple `impl`s satisfying `Vec<_>: Trait` found in the `unstable_impl_method_selection_aux` crate: - impl Trait for Vec; - impl Trait for Vec - where unstable feature: `bar`; + where feature(bar) is enabled; error: aborting due to 1 previous error From a9554b4d5f2666b0bce66dfb8f70a41092c9265f Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Wed, 24 Sep 2025 20:02:34 +0100 Subject: [PATCH 8/9] Clarify Display for error should not include source --- library/core/src/error.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 92b3c83d1bf34..9ca91ee009ee9 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -16,13 +16,19 @@ use crate::fmt::{self, Debug, Display, Formatter}; /// assert_eq!(err.to_string(), "invalid digit found in string"); /// ``` /// +/// # Error source +/// /// Errors may provide cause information. [`Error::source()`] is generally /// used when errors cross "abstraction boundaries". If one module must report /// an error that is caused by an error from a lower-level module, it can allow -/// accessing that error via [`Error::source()`]. This makes it possible for the +/// accessing that error via `Error::source()`. This makes it possible for the /// high-level module to provide its own errors while also revealing some of the /// implementation for debugging. /// +/// In error types that wrap an underlying error, the underlying error +/// should be either returned by the outer error's `Error::source()`, or rendered +/// by the outer error's `Display` implementation, but not both. +/// /// # Example /// /// Implementing the `Error` trait only requires that `Debug` and `Display` are implemented too. From 92859e98ee1a2101df8322a8581e4d638eb15078 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Wed, 24 Sep 2025 20:32:50 +0100 Subject: [PATCH 9/9] Repro duration_since regression from issue 146228 --- library/std/src/sys/pal/hermit/time.rs | 11 +++++++++-- library/std/src/sys/pal/unix/time.rs | 27 ++++++++++++-------------- library/std/tests/time.rs | 16 +++++++++++++++ 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index f76a5f96c8750..bd6fd5a3de428 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -26,15 +26,22 @@ impl Timespec { } fn sub_timespec(&self, other: &Timespec) -> Result { + fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 { + debug_assert!(a >= b); + a.wrapping_sub(b).cast_unsigned() + } + if self >= other { + // Logic here is identical to Unix version of `Timestamp::sub_timespec`, + // check comments there why operations do not overflow. Ok(if self.t.tv_nsec >= other.t.tv_nsec { Duration::new( - (self.t.tv_sec - other.t.tv_sec) as u64, + sub_ge_to_unsigned(self.t.tv_sec, other.t.tv_sec), (self.t.tv_nsec - other.t.tv_nsec) as u32, ) } else { Duration::new( - (self.t.tv_sec - 1 - other.t.tv_sec) as u64, + sub_ge_to_unsigned(self.t.tv_sec - 1, other.t.tv_sec), (self.t.tv_nsec + NSEC_PER_SEC - other.t.tv_nsec) as u32, ) }) diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index bd7f74fea6a9c..c207f41cad4b5 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -134,28 +134,25 @@ impl Timespec { } pub fn sub_timespec(&self, other: &Timespec) -> Result { + // When a >= b, the difference fits in u64. + fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 { + debug_assert!(a >= b); + a.wrapping_sub(b).cast_unsigned() + } + if self >= other { - // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM - // to optimize it into a branchless form (see also #75545): - // - // 1. `self.tv_sec - other.tv_sec` shows up as a common expression - // in both branches, i.e. the `else` must have its `- 1` - // subtraction after the common one, not interleaved with it - // (it used to be `self.tv_sec - 1 - other.tv_sec`) - // - // 2. the `Duration::new` call (or any other additional complexity) - // is outside of the `if`-`else`, not duplicated in both branches - // - // Ideally this code could be rearranged such that it more - // directly expresses the lower-cost behavior we want from it. let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() { ( - (self.tv_sec - other.tv_sec) as u64, + sub_ge_to_unsigned(self.tv_sec, other.tv_sec), self.tv_nsec.as_inner() - other.tv_nsec.as_inner(), ) } else { + // Following sequence of assertions explain why `self.tv_sec - 1` does not underflow. + debug_assert!(self.tv_nsec < other.tv_nsec); + debug_assert!(self.tv_sec > other.tv_sec); + debug_assert!(self.tv_sec > i64::MIN); ( - (self.tv_sec - other.tv_sec - 1) as u64, + sub_ge_to_unsigned(self.tv_sec - 1, other.tv_sec), self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(), ) }; diff --git a/library/std/tests/time.rs b/library/std/tests/time.rs index 40709eae37cfc..be1948af91564 100644 --- a/library/std/tests/time.rs +++ b/library/std/tests/time.rs @@ -227,3 +227,19 @@ fn big_math() { check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub); check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub); } + +#[test] +#[cfg(unix)] +fn system_time_duration_since_max_range_on_unix() { + // Repro regression https://github.com/rust-lang/rust/issues/146228 + + // Min and max values of `SystemTime` on Unix. + let min = SystemTime::UNIX_EPOCH - (Duration::new(i64::MAX as u64 + 1, 0)); + let max = SystemTime::UNIX_EPOCH + (Duration::new(i64::MAX as u64, 999_999_999)); + + let delta_a = max.duration_since(min).expect("duration_since overflow"); + let delta_b = min.duration_since(max).expect_err("duration_since overflow").duration(); + + assert_eq!(Duration::MAX, delta_a); + assert_eq!(Duration::MAX, delta_b); +}