From ff7b6cc27956d1d51e6651c3207fa97c9496e2bb Mon Sep 17 00:00:00 2001 From: Voxell Paladynee Date: Tue, 11 Nov 2025 04:33:12 +0100 Subject: [PATCH 01/12] doc(core::panic::Location::caller): clarify semantics with visual example without changing doctests --- library/core/src/panic/location.rs | 80 +++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 8176af03d13a5..f37f5370997e1 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -105,38 +105,72 @@ impl<'a> Location<'a> { /// ```standalone_crate /// use std::panic::Location; /// - /// /// Returns the [`Location`] at which it is called. + /// /// ``` + /// /// |1 |11 |21 |31 |41 + /// /// +-|---------|---------|---------|---------|-------- + /// /// 15 | #[track_caller] + /// /// 16 | fn new_location() -> &'static Location<'static> { + /// /// 17 | Location::caller() + /// /// | ------------------| the value of this expression depends on the caller, + /// /// | | since the function is marked #[track_caller] + /// /// 18 | } + /// /// ``` /// #[track_caller] - /// fn get_caller_location() -> &'static Location<'static> { + /// fn new_location() -> &'static Location<'static> { /// Location::caller() /// } /// - /// /// Returns a [`Location`] from within this function's definition. - /// fn get_just_one_location() -> &'static Location<'static> { - /// get_caller_location() + /// /// ``` + /// /// |1 |5 |11 |21 |31 |41 |51 + /// /// +-|---|-----|---------|---------|---------|---------|--- + /// /// 29 | fn constant_location() -> &'static Location<'static> { + /// /// 30 | new_location() + /// /// | ^ any invocation of constant_location() points here, + /// /// | no matter the location it is called from + /// /// 31 | } + /// /// ``` + /// fn constant_location() -> &'static Location<'static> { + /// new_location() /// } /// - /// let fixed_location = get_just_one_location(); - /// assert_eq!(fixed_location.file(), file!()); - /// assert_eq!(fixed_location.line(), 14); - /// assert_eq!(fixed_location.column(), 5); + /// fn main() { + /// // |1 |5 |11 |21 |31 |41 |51 + /// // +-|---|-----|---------|---------|---------|---------|--- + /// // 29 | fn constant_location() -> &'static Location<'static> { + /// // 30 | new_location() + /// // | ^ `let constant` points here + /// // 31 | } + /// let constant = constant_location(); + /// assert_eq!(constant.file(), file!()); + /// assert_eq!((constant.line(), constant.column()), (30, 5)); /// - /// // running the same untracked function in a different location gives us the same result - /// let second_fixed_location = get_just_one_location(); - /// assert_eq!(fixed_location.file(), second_fixed_location.file()); - /// assert_eq!(fixed_location.line(), second_fixed_location.line()); - /// assert_eq!(fixed_location.column(), second_fixed_location.column()); + /// let constant_2 = constant_location(); + /// assert_eq!( + /// (constant.file(), constant.line(), constant.column()), + /// (constant_2.file(), constant_2.line(), constant_2.column()) + /// ); /// - /// let this_location = get_caller_location(); - /// assert_eq!(this_location.file(), file!()); - /// assert_eq!(this_location.line(), 28); - /// assert_eq!(this_location.column(), 21); + /// // |1 |11 |16 |21 |31 + /// // +-|---------|----|----|---------|------ + /// // 55 | let here = new_location(); + /// // | ^ `let here` points here, as `new_location()` is the callsite + /// // 56 | assert_eq!(here.file(), file!()); + /// let here = new_location(); + /// assert_eq!(here.file(), file!()); + /// assert_eq!((here.line(), here.column()), (55, 16)); /// - /// // running the tracked function in a different location produces a different value - /// let another_location = get_caller_location(); - /// assert_eq!(this_location.file(), another_location.file()); - /// assert_ne!(this_location.line(), another_location.line()); - /// assert_ne!(this_location.column(), another_location.column()); + /// // |1 |11 |21 ||32 |41 |51 + /// // +-|---------|---------|---------||--------|---------|------ + /// // 64 | let yet_another_location = new_location(); + /// // | ^ `let yet_another_location` points here + /// // 65 | assert_eq!(here.file(), yet_another_location.file()); + /// let yet_another_location = new_location(); + /// assert_eq!(here.file(), yet_another_location.file()); + /// assert_ne!( + /// (here.line(), here.column()), + /// (yet_another_location.line(), yet_another_location.column()) + /// ); + /// } /// ``` #[must_use] #[stable(feature = "track_caller", since = "1.46.0")] From 5a8cf3362c29cbc503d9ea1ec8c119578da38fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A1n=20de=20B=C3=BArca?= Date: Fri, 7 Nov 2025 09:47:55 -0800 Subject: [PATCH 02/12] alloc: Document panics when allocations will exceed max --- library/alloc/src/string.rs | 21 +++++++++++++++++---- library/alloc/src/vec/mod.rs | 9 +++++++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 4a2689e01ff17..f5ba71c288334 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -454,6 +454,10 @@ impl String { /// /// [`new`]: String::new /// + /// # Panics + /// + /// Panics if the capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -1079,6 +1083,10 @@ impl String { /// Appends a given string slice onto the end of this `String`. /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -1101,8 +1109,9 @@ impl String { /// /// # Panics /// - /// Panics if the range has `start_bound > end_bound`, or, if the range is - /// bounded on either end and does not lie on a [`char`] boundary. + /// Panics if the range has `start_bound > end_bound`, if the range is + /// bounded on either end and does not lie on a [`char`] boundary, or if the + /// new capacity exceeds `isize::MAX` bytes. /// /// # Examples /// @@ -1158,7 +1167,7 @@ impl String { /// /// # Panics /// - /// Panics if the new capacity overflows [`usize`]. + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. /// /// # Examples /// @@ -1208,7 +1217,7 @@ impl String { /// /// # Panics /// - /// Panics if the new capacity overflows [`usize`]. + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. /// /// # Examples /// @@ -1372,6 +1381,10 @@ impl String { /// Appends the given [`char`] to the end of this `String`. /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 13d38d3c9609a..0c485ed31912a 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3338,6 +3338,10 @@ impl Vec { /// except that it also works with slice elements that are Clone but not Copy. /// If Rust gets specialization this function may be deprecated. /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -3359,8 +3363,9 @@ impl Vec { /// /// # Panics /// - /// Panics if starting index is greater than the end index - /// or if the index is greater than the length of the vector. + /// Panics if starting index is greater than the end index, if the index is + /// greater than the length of the vector, or if the new capacity exceeds + /// `isize::MAX` _bytes_. /// /// # Examples /// From bb239c290c1283d576fde8d87b2e514cf2dec9b9 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 1 Dec 2025 21:51:44 -0500 Subject: [PATCH 03/12] library: Rename `IterRange*` to `Range*Iter` There is a weak convention in the ecosystem that `IterFoos` is an iterator yielding items of type `Foo` (e.g. `bitflags` `IterNames`, `hashbrown` `IterBuckets`), while `FooIter` is an iterator over `Foo` from an `.iter()` or `.into_iter()` method (e.g. `memchr` `OneIter`, `regex` `SetMatchesIter`). Rename `IterRange`, `IterRangeInclusive`, and `IterRangeFrom` to `RangeIter`, `RangeInclusiveIter`, and `RangeInclusiveIter` to match this. Tracking issue: RUST-125687 (`new_range_api`) --- library/core/src/range.rs | 8 +-- library/core/src/range/iter.rs | 54 +++++++++---------- ...ks.rs => fromrangeiter-overflow-checks.rs} | 4 +- .../{iterrangefrom.rs => fromrangeiter.rs} | 0 tests/ui/new-range/enabled.rs | 6 +-- 5 files changed, 36 insertions(+), 36 deletions(-) rename tests/codegen-llvm/{iterrangefrom-overflow-checks.rs => fromrangeiter-overflow-checks.rs} (90%) rename tests/ui/iterators/{iterrangefrom.rs => fromrangeiter.rs} (100%) diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 2df520d35b398..4b87d426bda76 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -26,7 +26,7 @@ pub mod legacy; use Bound::{Excluded, Included, Unbounded}; #[doc(inline)] -pub use iter::{IterRange, IterRangeFrom, IterRangeInclusive}; +pub use iter::{RangeFromIter, RangeInclusiveIter, RangeIter}; #[doc(inline)] pub use crate::iter::Step; @@ -89,7 +89,7 @@ impl Range { /// ``` #[unstable(feature = "new_range_api", issue = "125687")] #[inline] - pub fn iter(&self) -> IterRange { + pub fn iter(&self) -> RangeIter { self.clone().into_iter() } } @@ -340,7 +340,7 @@ impl RangeInclusive { /// ``` #[unstable(feature = "new_range_api", issue = "125687")] #[inline] - pub fn iter(&self) -> IterRangeInclusive { + pub fn iter(&self) -> RangeInclusiveIter { self.clone().into_iter() } } @@ -477,7 +477,7 @@ impl RangeFrom { /// ``` #[unstable(feature = "new_range_api", issue = "125687")] #[inline] - pub fn iter(&self) -> IterRangeFrom { + pub fn iter(&self) -> RangeFromIter { self.clone().into_iter() } } diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index 9a8824baefe4e..6fe5d9b34361a 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -8,9 +8,9 @@ use crate::{intrinsics, mem}; /// By-value [`Range`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] #[derive(Debug, Clone)] -pub struct IterRange(legacy::Range); +pub struct RangeIter(legacy::Range); -impl IterRange { +impl RangeIter { /// Returns the remainder of the range being iterated over. pub fn remainder(self) -> Range { Range { start: self.0.start, end: self.0.end } @@ -23,11 +23,11 @@ macro_rules! unsafe_range_trusted_random_access_impl { ($($t:ty)*) => ($( #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] - unsafe impl TrustedRandomAccess for IterRange<$t> {} + unsafe impl TrustedRandomAccess for RangeIter<$t> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] - unsafe impl TrustedRandomAccessNoCoerce for IterRange<$t> { + unsafe impl TrustedRandomAccessNoCoerce for RangeIter<$t> { const MAY_HAVE_SIDE_EFFECT: bool = false; } )*) @@ -50,7 +50,7 @@ unsafe_range_trusted_random_access_impl! { } #[unstable(feature = "new_range_api", issue = "125687")] -impl Iterator for IterRange { +impl Iterator for RangeIter { type Item = A; #[inline] @@ -118,7 +118,7 @@ impl Iterator for IterRange { } #[unstable(feature = "new_range_api", issue = "125687")] -impl DoubleEndedIterator for IterRange { +impl DoubleEndedIterator for RangeIter { #[inline] fn next_back(&mut self) -> Option { self.0.next_back() @@ -136,27 +136,27 @@ impl DoubleEndedIterator for IterRange { } #[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IterRange {} +unsafe impl TrustedLen for RangeIter {} #[unstable(feature = "new_range_api", issue = "125687")] -impl FusedIterator for IterRange {} +impl FusedIterator for RangeIter {} #[unstable(feature = "new_range_api", issue = "125687")] impl IntoIterator for Range { type Item = A; - type IntoIter = IterRange; + type IntoIter = RangeIter; fn into_iter(self) -> Self::IntoIter { - IterRange(self.into()) + RangeIter(self.into()) } } /// By-value [`RangeInclusive`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] #[derive(Debug, Clone)] -pub struct IterRangeInclusive(legacy::RangeInclusive); +pub struct RangeInclusiveIter(legacy::RangeInclusive); -impl IterRangeInclusive { +impl RangeInclusiveIter { /// Returns the remainder of the range being iterated over. /// /// If the iterator is exhausted or empty, returns `None`. @@ -170,7 +170,7 @@ impl IterRangeInclusive { } #[unstable(feature = "new_range_api", issue = "125687")] -impl Iterator for IterRangeInclusive { +impl Iterator for RangeInclusiveIter { type Item = A; #[inline] @@ -226,7 +226,7 @@ impl Iterator for IterRangeInclusive { } #[unstable(feature = "new_range_api", issue = "125687")] -impl DoubleEndedIterator for IterRangeInclusive { +impl DoubleEndedIterator for RangeInclusiveIter { #[inline] fn next_back(&mut self) -> Option { self.0.next_back() @@ -244,18 +244,18 @@ impl DoubleEndedIterator for IterRangeInclusive { } #[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IterRangeInclusive {} +unsafe impl TrustedLen for RangeInclusiveIter {} #[unstable(feature = "new_range_api", issue = "125687")] -impl FusedIterator for IterRangeInclusive {} +impl FusedIterator for RangeInclusiveIter {} #[unstable(feature = "new_range_api", issue = "125687")] impl IntoIterator for RangeInclusive { type Item = A; - type IntoIter = IterRangeInclusive; + type IntoIter = RangeInclusiveIter; fn into_iter(self) -> Self::IntoIter { - IterRangeInclusive(self.into()) + RangeInclusiveIter(self.into()) } } @@ -270,14 +270,14 @@ impl IntoIterator for RangeInclusive { macro_rules! range_exact_iter_impl { ($($t:ty)*) => ($( #[unstable(feature = "new_range_api", issue = "125687")] - impl ExactSizeIterator for IterRange<$t> { } + impl ExactSizeIterator for RangeIter<$t> { } )*) } macro_rules! range_incl_exact_iter_impl { ($($t:ty)*) => ($( #[unstable(feature = "new_range_api", issue = "125687")] - impl ExactSizeIterator for IterRangeInclusive<$t> { } + impl ExactSizeIterator for RangeInclusiveIter<$t> { } )*) } @@ -294,14 +294,14 @@ range_incl_exact_iter_impl! { /// By-value [`RangeFrom`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] #[derive(Debug, Clone)] -pub struct IterRangeFrom { +pub struct RangeFromIter { start: A, /// Whether the first element of the iterator has yielded. /// Only used when overflow checks are enabled. first: bool, } -impl IterRangeFrom { +impl RangeFromIter { /// Returns the remainder of the range being iterated over. #[inline] #[rustc_inherit_overflow_checks] @@ -317,7 +317,7 @@ impl IterRangeFrom { } #[unstable(feature = "new_range_api", issue = "125687")] -impl Iterator for IterRangeFrom { +impl Iterator for RangeFromIter { type Item = A; #[inline] @@ -366,17 +366,17 @@ impl Iterator for IterRangeFrom { } #[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IterRangeFrom {} +unsafe impl TrustedLen for RangeFromIter {} #[unstable(feature = "new_range_api", issue = "125687")] -impl FusedIterator for IterRangeFrom {} +impl FusedIterator for RangeFromIter {} #[unstable(feature = "new_range_api", issue = "125687")] impl IntoIterator for RangeFrom { type Item = A; - type IntoIter = IterRangeFrom; + type IntoIter = RangeFromIter; fn into_iter(self) -> Self::IntoIter { - IterRangeFrom { start: self.start, first: true } + RangeFromIter { start: self.start, first: true } } } diff --git a/tests/codegen-llvm/iterrangefrom-overflow-checks.rs b/tests/codegen-llvm/fromrangeiter-overflow-checks.rs similarity index 90% rename from tests/codegen-llvm/iterrangefrom-overflow-checks.rs rename to tests/codegen-llvm/fromrangeiter-overflow-checks.rs index 88ff5a8508c8e..4d27f118ddd37 100644 --- a/tests/codegen-llvm/iterrangefrom-overflow-checks.rs +++ b/tests/codegen-llvm/fromrangeiter-overflow-checks.rs @@ -11,11 +11,11 @@ #![crate_type = "lib"] #![feature(new_range_api)] -use std::range::{IterRangeFrom, RangeFrom}; +use std::range::{RangeFrom, RangeFromIter}; // CHECK-LABEL: @iterrangefrom_remainder( #[no_mangle] -pub unsafe fn iterrangefrom_remainder(x: IterRangeFrom) -> RangeFrom { +pub unsafe fn iterrangefrom_remainder(x: RangeFromIter) -> RangeFrom { // DEBUG: i32 noundef %x // NOCHECKS: i32 noundef returned %x // DEBUG: br i1 diff --git a/tests/ui/iterators/iterrangefrom.rs b/tests/ui/iterators/fromrangeiter.rs similarity index 100% rename from tests/ui/iterators/iterrangefrom.rs rename to tests/ui/iterators/fromrangeiter.rs diff --git a/tests/ui/new-range/enabled.rs b/tests/ui/new-range/enabled.rs index 5ddbba492e763..140e3b6487097 100644 --- a/tests/ui/new-range/enabled.rs +++ b/tests/ui/new-range/enabled.rs @@ -17,8 +17,8 @@ fn main() { let c: core::range::RangeInclusive = 4..=5; let d: core::range::RangeToInclusive = ..=3; - let _: core::range::IterRangeFrom = a.into_iter(); - let _: core::range::IterRange = b.into_iter(); - let _: core::range::IterRangeInclusive = c.into_iter(); + let _: core::range::RangeFromIter = a.into_iter(); + let _: core::range::RangeIter = b.into_iter(); + let _: core::range::RangeInclusiveIter = c.into_iter(); // RangeToInclusive has no Iterator implementation } From e7734099d058ebf5ce89f8b5faeba65faf9942cc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 30 Nov 2025 11:59:43 +0100 Subject: [PATCH 04/12] float::maximum/minimum: make docs more streamlined --- library/core/src/num/f128.rs | 36 ++++++++++++++++++------------------ library/core/src/num/f16.rs | 36 ++++++++++++++++++------------------ library/core/src/num/f32.rs | 36 ++++++++++++++++++------------------ library/core/src/num/f64.rs | 36 ++++++++++++++++++------------------ 4 files changed, 72 insertions(+), 72 deletions(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index bfe3a501f4537..3832d7bb2c39a 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -750,8 +750,15 @@ impl f128 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f128::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f128::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(f128)] @@ -766,13 +773,6 @@ impl f128 { /// assert!(x.maximum(f128::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `maximum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f128", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] @@ -783,8 +783,15 @@ impl f128 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f128::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f128::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(f128)] @@ -799,13 +806,6 @@ impl f128 { /// assert!(x.minimum(f128::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `minimum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f128", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index d3a12e94c800c..a1b284620cbc2 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -741,8 +741,15 @@ impl f16 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f16::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f16::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(f16)] @@ -756,13 +763,6 @@ impl f16 { /// assert!(x.maximum(f16::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `maximum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f16", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] @@ -773,8 +773,15 @@ impl f16 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f16::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f16::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(f16)] @@ -788,13 +795,6 @@ impl f16 { /// assert!(x.minimum(f16::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `minimum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f16", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 7e6a757e5e297..b6ec994369562 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -943,8 +943,15 @@ impl f32 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f32::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f32::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -954,13 +961,6 @@ impl f32 { /// assert_eq!(x.maximum(y), y); /// assert!(x.maximum(f32::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `maximum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -970,8 +970,15 @@ impl f32 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f32::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f32::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -981,13 +988,6 @@ impl f32 { /// assert_eq!(x.minimum(y), x); /// assert!(x.minimum(f32::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `minimum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 854bdcf39d09e..42726fa57dc5d 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -961,8 +961,15 @@ impl f64 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f64::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f64::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -972,13 +979,6 @@ impl f64 { /// assert_eq!(x.maximum(y), y); /// assert!(x.maximum(f64::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `maximum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -988,8 +988,15 @@ impl f64 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f64::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f64::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -999,13 +1006,6 @@ impl f64 { /// assert_eq!(x.minimum(y), x); /// assert!(x.minimum(f64::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `minimum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] From f30eced2e3910139108f5751ea1f6a22829358c6 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Thu, 20 Nov 2025 02:06:38 +0900 Subject: [PATCH 05/12] Wrap binding name in parentheses in for-loop mut suggestion --- .../src/diagnostics/conflict_errors.rs | 16 ++++++++++++++- ...rowck-for-loop-deref-pattern-assignment.rs | 10 ++++++++++ ...k-for-loop-deref-pattern-assignment.stderr | 20 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs create mode 100644 tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 9a8927c102973..7bb50a3ec4d57 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3940,12 +3940,26 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(decl) = local_decl && decl.can_be_made_mutable() { + let message = if matches!( + decl.local_info(), + LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((_, match_span)), + .. + })) if matches!(match_span.desugaring_kind(), Some(DesugaringKind::ForLoop)) + ) && let Ok(binding_name) = + self.infcx.tcx.sess.source_map().span_to_snippet(decl.source_info.span) + { + format!("(mut {}) ", binding_name) + } else { + "mut ".to_string() + }; err.span_suggestion_verbose( decl.source_info.span.shrink_to_lo(), "consider making this binding mutable", - "mut ".to_string(), + message, Applicability::MachineApplicable, ); + if !from_arg && matches!( decl.local_info(), diff --git a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs new file mode 100644 index 0000000000000..fc4f1e4eacb95 --- /dev/null +++ b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs @@ -0,0 +1,10 @@ +//! regression test for +//! Ensure the diagnostic suggests `for &(mut x) ...` (parenthesized) instead of `&mut x`. + +fn main() { + let nums: &[u32] = &[1, 2, 3]; + for &num in nums { + num *= 2; //~ ERROR cannot assign twice to immutable variable `num` + println!("{num}"); + } +} diff --git a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr new file mode 100644 index 0000000000000..cd53e297e3483 --- /dev/null +++ b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr @@ -0,0 +1,20 @@ +error[E0384]: cannot assign twice to immutable variable `num` + --> $DIR/borrowck-for-loop-deref-pattern-assignment.rs:7:9 + | +LL | for &num in nums { + | --- first assignment to `num` +LL | num *= 2; + | ^^^^^^^^ cannot assign twice to immutable variable + | +help: consider making this binding mutable + | +LL | for &(mut num) num in nums { + | +++++++++ +help: to modify the original value, take a borrow instead + | +LL | for &ref mut num in nums { + | +++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0384`. From d49075f083d39c1ab91256c6945cc5c854ecee84 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Thu, 4 Dec 2025 15:13:40 +0900 Subject: [PATCH 06/12] Suppress `ref mut` suggestion for for-loop bindings --- .../src/diagnostics/conflict_errors.rs | 19 +++++++++++-------- ...k-for-loop-deref-pattern-assignment.stderr | 4 ---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7bb50a3ec4d57..f8a6fafbe78ac 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3940,14 +3940,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(decl) = local_decl && decl.can_be_made_mutable() { - let message = if matches!( - decl.local_info(), - LocalInfo::User(BindingForm::Var(VarBindingForm { - opt_match_place: Some((_, match_span)), - .. - })) if matches!(match_span.desugaring_kind(), Some(DesugaringKind::ForLoop)) - ) && let Ok(binding_name) = - self.infcx.tcx.sess.source_map().span_to_snippet(decl.source_info.span) + let is_for_loop = matches!( + decl.local_info(), + LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((_, match_span)), + .. + })) if matches!(match_span.desugaring_kind(), Some(DesugaringKind::ForLoop)) + ); + let message = if is_for_loop + && let Ok(binding_name) = + self.infcx.tcx.sess.source_map().span_to_snippet(decl.source_info.span) { format!("(mut {}) ", binding_name) } else { @@ -3961,6 +3963,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); if !from_arg + && !is_for_loop && matches!( decl.local_info(), LocalInfo::User(BindingForm::Var(VarBindingForm { diff --git a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr index cd53e297e3483..fa230134df555 100644 --- a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr +++ b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr @@ -10,10 +10,6 @@ help: consider making this binding mutable | LL | for &(mut num) num in nums { | +++++++++ -help: to modify the original value, take a borrow instead - | -LL | for &ref mut num in nums { - | +++++++ error: aborting due to 1 previous error From e513ce3fb47b6dd89f37edf64f11720f6d1e7dfe Mon Sep 17 00:00:00 2001 From: Wafarm Date: Thu, 4 Dec 2025 14:58:30 +0800 Subject: [PATCH 07/12] Check identifiers defined in macros when suggesting identifiers hidden by hygiene --- .../rustc_resolve/src/late/diagnostics.rs | 14 +++++++ tests/ui/hygiene/pattern-macro.stderr | 10 +++++ .../macros/macro-hygiene-help-issue-149604.rs | 9 +++++ .../macro-hygiene-help-issue-149604.stderr | 38 +++++++++++++++++++ .../proc-macro/gen-macro-rules-hygiene.stderr | 10 +++++ tests/ui/proc-macro/mixed-site-span.stderr | 7 ++++ 6 files changed, 88 insertions(+) create mode 100644 tests/ui/macros/macro-hygiene-help-issue-149604.rs create mode 100644 tests/ui/macros/macro-hygiene-help-issue-149604.stderr diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 2b85639376d97..3ab6c7dcc0066 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1155,6 +1155,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let callsite_span = span.source_callsite(); for rib in self.ribs[ValueNS].iter().rev() { for (binding_ident, _) in &rib.bindings { + // Case 1: the identifier is defined in the same scope as the macro is called if binding_ident.name == ident.name && !binding_ident.span.eq_ctxt(span) && !binding_ident.span.from_expansion() @@ -1166,6 +1167,19 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { ); return; } + + // Case 2: the identifier is defined in a macro call in the same scope + if binding_ident.name == ident.name + && binding_ident.span.from_expansion() + && binding_ident.span.source_callsite().eq_ctxt(callsite_span) + && binding_ident.span.source_callsite().lo() < callsite_span.lo() + { + err.span_help( + binding_ident.span, + "an identifier with the same name is defined here, but is not accessible due to macro hygiene", + ); + return; + } } } } diff --git a/tests/ui/hygiene/pattern-macro.stderr b/tests/ui/hygiene/pattern-macro.stderr index a9764cea49e59..047244ba9edef 100644 --- a/tests/ui/hygiene/pattern-macro.stderr +++ b/tests/ui/hygiene/pattern-macro.stderr @@ -3,6 +3,16 @@ error[E0425]: cannot find value `x` in this scope | LL | x + 1; | ^ not found in this scope + | +help: an identifier with the same name is defined here, but is not accessible due to macro hygiene + --> $DIR/pattern-macro.rs:1:28 + | +LL | macro_rules! foo { () => ( x ) } + | ^ +... +LL | let foo!() = 2; + | ------ in this macro invocation + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/macros/macro-hygiene-help-issue-149604.rs b/tests/ui/macros/macro-hygiene-help-issue-149604.rs new file mode 100644 index 0000000000000..5700218cc8671 --- /dev/null +++ b/tests/ui/macros/macro-hygiene-help-issue-149604.rs @@ -0,0 +1,9 @@ +macro_rules! let_it { {} => { let it = (); } } +macro_rules! print_it { {} => { println!("{:?}", it); } } +//~^ ERROR cannot find value `it` in this scope + +fn main() { + let_it!(); + let () = it; //~ ERROR cannot find value `it` in this scope + print_it!(); +} diff --git a/tests/ui/macros/macro-hygiene-help-issue-149604.stderr b/tests/ui/macros/macro-hygiene-help-issue-149604.stderr new file mode 100644 index 0000000000000..dc95cb7a43f00 --- /dev/null +++ b/tests/ui/macros/macro-hygiene-help-issue-149604.stderr @@ -0,0 +1,38 @@ +error[E0425]: cannot find value `it` in this scope + --> $DIR/macro-hygiene-help-issue-149604.rs:7:14 + | +LL | let () = it; + | ^^ not found in this scope + | +help: an identifier with the same name is defined here, but is not accessible due to macro hygiene + --> $DIR/macro-hygiene-help-issue-149604.rs:1:35 + | +LL | macro_rules! let_it { {} => { let it = (); } } + | ^^ +... +LL | let_it!(); + | --------- in this macro invocation + = note: this error originates in the macro `let_it` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0425]: cannot find value `it` in this scope + --> $DIR/macro-hygiene-help-issue-149604.rs:2:50 + | +LL | macro_rules! print_it { {} => { println!("{:?}", it); } } + | ^^ not found in this scope +... +LL | print_it!(); + | ----------- in this macro invocation + | +help: an identifier with the same name is defined here, but is not accessible due to macro hygiene + --> $DIR/macro-hygiene-help-issue-149604.rs:1:35 + | +LL | macro_rules! let_it { {} => { let it = (); } } + | ^^ +... +LL | let_it!(); + | --------- in this macro invocation + = note: this error originates in the macro `print_it` which comes from the expansion of the macro `let_it` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr b/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr index 17171ad5c5cc5..ed8ee4dc52cba 100644 --- a/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr +++ b/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr @@ -30,6 +30,16 @@ error[E0425]: cannot find value `local_def` in this scope | LL | local_def; | ^^^^^^^^^ help: a local variable with a similar name exists: `local_use` + | +help: an identifier with the same name is defined here, but is not accessible due to macro hygiene + --> $DIR/gen-macro-rules-hygiene.rs:13:1 + | +LL | gen_macro_rules!(); + | ^^^^^^^^^^^^^^^^^^ +... +LL | generated!(); + | ------------ in this macro invocation + = note: this error originates in the macro `generated` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/proc-macro/mixed-site-span.stderr b/tests/ui/proc-macro/mixed-site-span.stderr index d5cf484f6dd0e..97e3f3e3dea8c 100644 --- a/tests/ui/proc-macro/mixed-site-span.stderr +++ b/tests/ui/proc-macro/mixed-site-span.stderr @@ -606,6 +606,13 @@ error[E0425]: cannot find value `local_def` in this scope | LL | local_def; | ^^^^^^^^^ help: a local variable with a similar name exists: `local_use` + | +help: an identifier with the same name is defined here, but is not accessible due to macro hygiene + --> $DIR/mixed-site-span.rs:23:9 + | +LL | proc_macro_rules!(); + | ^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `proc_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 52 previous errors From 3d0f5f2f88678b894ebafad599852e129229e5d8 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Fri, 5 Dec 2025 00:39:55 +0900 Subject: [PATCH 08/12] Add regression test for 141845 --- ...solution-with-inherent-associated-types.rs | 13 +++++++++++++ ...tion-with-inherent-associated-types.stderr | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.rs create mode 100644 tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.stderr diff --git a/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.rs b/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.rs new file mode 100644 index 0000000000000..6a8d291e3cc00 --- /dev/null +++ b/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.rs @@ -0,0 +1,13 @@ +//! Regression test for +//! Checks const resolution stability when using inherent associated types +//! and generic const arguments. + +//@compile-flags: --crate-type=lib +#![expect(incomplete_features)] +#![feature(inherent_associated_types, min_generic_const_args)] +trait Trait {} + +struct Struct; + +type Alias = Struct<{ Struct::N }>; +//~^ ERROR: missing generics for struct `Struct` [E0107] diff --git a/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.stderr b/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.stderr new file mode 100644 index 0000000000000..13e73dafbea65 --- /dev/null +++ b/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.stderr @@ -0,0 +1,19 @@ +error[E0107]: missing generics for struct `Struct` + --> $DIR/resolution-with-inherent-associated-types.rs:12:33 + | +LL | type Alias = Struct<{ Struct::N }>; + | ^^^^^^ expected 1 generic argument + | +note: struct defined here, with 1 generic parameter: `N` + --> $DIR/resolution-with-inherent-associated-types.rs:10:8 + | +LL | struct Struct; + | ^^^^^^ -------------- +help: add missing generic argument + | +LL | type Alias = Struct<{ Struct::N }>; + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0107`. From ad73972e999b79ea4e17d997676bdb8327288c77 Mon Sep 17 00:00:00 2001 From: Alina Sbirlea Date: Thu, 4 Dec 2025 20:21:49 +0000 Subject: [PATCH 09/12] Fix for LLVM22 making lowering decisions dependent on RuntimeLibraryInfo. LLVM reference commit: https://github.com/llvm/llvm-project/commit/04c81a99735c04b2018eeb687e74f9860e1d0e1b. --- compiler/rustc_codegen_llvm/src/back/write.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 12 +++++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index c0c01b80372f3..b131de1df8ba3 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -75,7 +75,7 @@ fn write_output_file<'ll>( let result = unsafe { let pm = llvm::LLVMCreatePassManager(); llvm::LLVMAddAnalysisPasses(target, pm); - llvm::LLVMRustAddLibraryInfo(pm, m, no_builtins); + llvm::LLVMRustAddLibraryInfo(target, pm, m, no_builtins); llvm::LLVMRustWriteOutputFile( target, pm, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 53b9a2bda8944..c5cbc92ae772d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2379,6 +2379,7 @@ unsafe extern "C" { ) -> *mut TargetMachine; pub(crate) fn LLVMRustAddLibraryInfo<'a>( + T: &TargetMachine, PM: &PassManager<'a>, M: &'a Module, DisableSimplifyLibCalls: bool, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 143cc94790890..714ba0f177a8f 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -6,6 +6,9 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/Lint.h" #include "llvm/Analysis/TargetLibraryInfo.h" +#if LLVM_VERSION_GE(22, 0) +#include "llvm/Analysis/RuntimeLibcallInfo.h" +#endif #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/Bitcode/BitcodeWriterPass.h" #include "llvm/CodeGen/CommandFlags.h" @@ -379,13 +382,20 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( // Unfortunately, the LLVM C API doesn't provide a way to create the // TargetLibraryInfo pass, so we use this method to do so. -extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M, +extern "C" void LLVMRustAddLibraryInfo(LLVMTargetMachineRef T, + LLVMPassManagerRef PMR, LLVMModuleRef M, bool DisableSimplifyLibCalls) { auto TargetTriple = Triple(unwrap(M)->getTargetTriple()); + TargetOptions *Options = &unwrap(T)->Options; auto TLII = TargetLibraryInfoImpl(TargetTriple); if (DisableSimplifyLibCalls) TLII.disableAllFunctions(); unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII)); +#if LLVM_VERSION_GE(22, 0) + unwrap(PMR)->add(new RuntimeLibraryInfoWrapper( + TargetTriple, Options->ExceptionModel, Options->FloatABIType, + Options->EABIVersion, Options->MCOptions.ABIName, Options->VecLib)); +#endif } extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) { From 84ff44c74933374a941f0c8c49e8381d830c3193 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 5 Dec 2025 13:44:03 +1100 Subject: [PATCH 10/12] Add perma-unstable `--print=backend-has-zstd` for use by compiletest Tests for `-Zdebuginfo-compression=zstd` need to be skipped if LLVM was built without support for zstd compression. Currently, compiletest relies on messy and fragile heuristics to detect whether the compiler's LLVM was built with zstd support. But the compiler itself already knows whether LLVM has zstd or not, so it's easier for compiletest to just ask the compiler. --- compiler/rustc_codegen_llvm/src/lib.rs | 4 + compiler/rustc_driver_impl/src/lib.rs | 1 + .../rustc_session/src/config/print_request.rs | 3 + src/tools/compiletest/src/common.rs | 6 +- src/tools/compiletest/src/directives.rs | 101 ------------------ src/tools/compiletest/src/directives/needs.rs | 23 +++- .../help-diff.diff | 2 +- .../unstable-invalid-print-request-help.err | 2 +- tests/run-make/rustc-help/help-v.stdout | 2 +- tests/run-make/rustc-help/help.stdout | 2 +- .../print-without-arg.stderr | 2 +- tests/ui/invalid-compile-flags/print.stderr | 2 +- .../backend-has-zstd-unstable.rs | 8 ++ .../backend-has-zstd-unstable.stderr | 2 + .../ui/print-request/print-lints-help.stderr | 2 +- 15 files changed, 50 insertions(+), 112 deletions(-) create mode 100644 tests/ui/print-request/backend-has-zstd-unstable.rs create mode 100644 tests/ui/print-request/backend-has-zstd-unstable.stderr diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 1b65a133d58c1..8c0c0afcc1dd7 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -257,6 +257,10 @@ impl CodegenBackend for LlvmCodegenBackend { } writeln!(out).unwrap(); } + PrintKind::BackendHasZstd => { + let has_zstd = llvm::LLVMRustLLVMHasZstdCompression(); + writeln!(out, "{has_zstd}").unwrap(); + } PrintKind::CodeModels => { writeln!(out, "Available code models:").unwrap(); for name in &["tiny", "small", "kernel", "medium", "large"] { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 9a3d7cc506cf7..0853f638509fd 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -799,6 +799,7 @@ fn print_crate_info( println_info!("{}", calling_conventions.join("\n")); } RelocationModels + | BackendHasZstd | CodeModels | TlsModels | TargetCPUs diff --git a/compiler/rustc_session/src/config/print_request.rs b/compiler/rustc_session/src/config/print_request.rs index b8111fbc17f84..395f7a4fab71f 100644 --- a/compiler/rustc_session/src/config/print_request.rs +++ b/compiler/rustc_session/src/config/print_request.rs @@ -22,6 +22,7 @@ pub struct PrintRequest { pub enum PrintKind { // tidy-alphabetical-start AllTargetSpecsJson, + BackendHasZstd, CallingConventions, Cfg, CheckCfg, @@ -59,6 +60,7 @@ impl PrintKind { match self { // tidy-alphabetical-start AllTargetSpecsJson => "all-target-specs-json", + BackendHasZstd => "backend-has-zstd", CallingConventions => "calling-conventions", Cfg => "cfg", CheckCfg => "check-cfg", @@ -111,6 +113,7 @@ impl PrintKind { // Unstable values: AllTargetSpecsJson => false, + BackendHasZstd => false, // (perma-unstable, for use by compiletest) CheckCfg => false, CrateRootLintLevels => false, SupportedCrateTypes => false, diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index eb1fc55a26240..d8472691afdf7 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -1096,7 +1096,11 @@ fn supported_crate_types(config: &Config) -> HashSet { crate_types } -fn query_rustc_output(config: &Config, args: &[&str], envs: HashMap) -> String { +pub(crate) fn query_rustc_output( + config: &Config, + args: &[&str], + envs: HashMap, +) -> String { let query_rustc_path = config.query_rustc_path.as_deref().unwrap_or(&config.rustc_path); let mut command = Command::new(query_rustc_path); diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index fed30415de569..c154886ebcdec 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -885,107 +885,6 @@ pub fn extract_llvm_version_from_binary(binary_path: &str) -> Option { None } -/// For tests using the `needs-llvm-zstd` directive: -/// - for local LLVM builds, try to find the static zstd library in the llvm-config system libs. -/// - for `download-ci-llvm`, see if `lld` was built with zstd support. -pub fn llvm_has_libzstd(config: &Config) -> bool { - // Strategy 1: works for local builds but not with `download-ci-llvm`. - // - // We check whether `llvm-config` returns the zstd library. Bootstrap's `llvm.libzstd` will only - // ask to statically link it when building LLVM, so we only check if the list of system libs - // contains a path to that static lib, and that it exists. - // - // See compiler/rustc_llvm/build.rs for more details and similar expectations. - fn is_zstd_in_config(llvm_bin_dir: &Utf8Path) -> Option<()> { - let llvm_config_path = llvm_bin_dir.join("llvm-config"); - let output = Command::new(llvm_config_path).arg("--system-libs").output().ok()?; - assert!(output.status.success(), "running llvm-config --system-libs failed"); - - let libs = String::from_utf8(output.stdout).ok()?; - for lib in libs.split_whitespace() { - if lib.ends_with("libzstd.a") && Utf8Path::new(lib).exists() { - return Some(()); - } - } - - None - } - - // Strategy 2: `download-ci-llvm`'s `llvm-config --system-libs` will not return any libs to - // use. - // - // The CI artifacts also don't contain the bootstrap config used to build them: otherwise we - // could have looked at the `llvm.libzstd` config. - // - // We infer whether `LLVM_ENABLE_ZSTD` was used to build LLVM as a byproduct of testing whether - // `lld` supports it. If not, an error will be emitted: "LLVM was not built with - // LLVM_ENABLE_ZSTD or did not find zstd at build time". - #[cfg(unix)] - fn is_lld_built_with_zstd(llvm_bin_dir: &Utf8Path) -> Option<()> { - let lld_path = llvm_bin_dir.join("lld"); - if lld_path.exists() { - // We can't call `lld` as-is, it expects to be invoked by a compiler driver using a - // different name. Prepare a temporary symlink to do that. - let lld_symlink_path = llvm_bin_dir.join("ld.lld"); - if !lld_symlink_path.exists() { - std::os::unix::fs::symlink(lld_path, &lld_symlink_path).ok()?; - } - - // Run `lld` with a zstd flag. We expect this command to always error here, we don't - // want to link actual files and don't pass any. - let output = Command::new(&lld_symlink_path) - .arg("--compress-debug-sections=zstd") - .output() - .ok()?; - assert!(!output.status.success()); - - // Look for a specific error caused by LLVM not being built with zstd support. We could - // also look for the "no input files" message, indicating the zstd flag was accepted. - let stderr = String::from_utf8(output.stderr).ok()?; - let zstd_available = !stderr.contains("LLVM was not built with LLVM_ENABLE_ZSTD"); - - // We don't particularly need to clean the link up (so the previous commands could fail - // in theory but won't in practice), but we can try. - std::fs::remove_file(lld_symlink_path).ok()?; - - if zstd_available { - return Some(()); - } - } - - None - } - - #[cfg(not(unix))] - fn is_lld_built_with_zstd(_llvm_bin_dir: &Utf8Path) -> Option<()> { - None - } - - if let Some(llvm_bin_dir) = &config.llvm_bin_dir { - // Strategy 1: for local LLVM builds. - if is_zstd_in_config(llvm_bin_dir).is_some() { - return true; - } - - // Strategy 2: for LLVM artifacts built on CI via `download-ci-llvm`. - // - // It doesn't work for cases where the artifacts don't contain the linker, but it's - // best-effort: CI has `llvm.libzstd` and `lld` enabled on the x64 linux artifacts, so it - // will at least work there. - // - // If this can be improved and expanded to less common cases in the future, it should. - if config.target == "x86_64-unknown-linux-gnu" - && config.host == config.target - && is_lld_built_with_zstd(llvm_bin_dir).is_some() - { - return true; - } - } - - // Otherwise, all hope is lost. - false -} - /// Takes a directive of the form `" [- ]"`, returns the numeric representation /// of `` and `` as tuple: `(, )`. /// diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index b544006210928..208e96166021e 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -1,5 +1,7 @@ -use crate::common::{Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer}; -use crate::directives::{DirectiveLine, IgnoreDecision, llvm_has_libzstd}; +use crate::common::{ + Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer, query_rustc_output, +}; +use crate::directives::{DirectiveLine, IgnoreDecision}; pub(super) fn handle_needs( cache: &CachedNeedsConditions, @@ -377,7 +379,7 @@ impl CachedNeedsConditions { .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" }) .exists(), - llvm_zstd: llvm_has_libzstd(&config), + llvm_zstd: llvm_has_zstd(&config), dlltool: find_dlltool(&config), symlinks: has_symlinks(), } @@ -428,3 +430,18 @@ fn has_symlinks() -> bool { fn has_symlinks() -> bool { true } + +fn llvm_has_zstd(config: &Config) -> bool { + // The compiler already knows whether LLVM was built with zstd or not, + // so compiletest can just ask the compiler. + let output = query_rustc_output( + config, + &["-Zunstable-options", "--print=backend-has-zstd"], + Default::default(), + ); + match output.trim() { + "true" => true, + "false" => false, + _ => panic!("unexpected output from `--print=backend-has-zstd`: {output:?}"), + } +} diff --git a/tests/run-make/print-request-help-stable-unstable/help-diff.diff b/tests/run-make/print-request-help-stable-unstable/help-diff.diff index 044302a19a016..e382a24782711 100644 --- a/tests/run-make/print-request-help-stable-unstable/help-diff.diff +++ b/tests/run-make/print-request-help-stable-unstable/help-diff.diff @@ -2,6 +2,6 @@ error: unknown print request: `xxx` | - = help: valid print requests are: `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `tls-models` -+ = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` ++ = help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err b/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err index cc6c3c909b366..70764ea13aa87 100644 --- a/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err +++ b/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err @@ -1,5 +1,5 @@ error: unknown print request: `xxx` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/run-make/rustc-help/help-v.stdout b/tests/run-make/rustc-help/help-v.stdout index cd161c51ee3b9..c41cb5e3bde87 100644 --- a/tests/run-make/rustc-help/help-v.stdout +++ b/tests/run-make/rustc-help/help-v.stdout @@ -43,7 +43,7 @@ Options: --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 -o Write output to FILENAME diff --git a/tests/run-make/rustc-help/help.stdout b/tests/run-make/rustc-help/help.stdout index 74ec083bdee7a..5e13a900484de 100644 --- a/tests/run-make/rustc-help/help.stdout +++ b/tests/run-make/rustc-help/help.stdout @@ -43,7 +43,7 @@ Options: --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 -o Write output to FILENAME diff --git a/tests/ui/invalid-compile-flags/print-without-arg.stderr b/tests/ui/invalid-compile-flags/print-without-arg.stderr index 4163d4e060220..ff9669614360a 100644 --- a/tests/ui/invalid-compile-flags/print-without-arg.stderr +++ b/tests/ui/invalid-compile-flags/print-without-arg.stderr @@ -3,5 +3,5 @@ error: Argument to option 'print' missing --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . diff --git a/tests/ui/invalid-compile-flags/print.stderr b/tests/ui/invalid-compile-flags/print.stderr index e8adbfd87d761..e2521ebf26a41 100644 --- a/tests/ui/invalid-compile-flags/print.stderr +++ b/tests/ui/invalid-compile-flags/print.stderr @@ -1,5 +1,5 @@ error: unknown print request: `yyyy` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/ui/print-request/backend-has-zstd-unstable.rs b/tests/ui/print-request/backend-has-zstd-unstable.rs new file mode 100644 index 0000000000000..cfed4af0c794f --- /dev/null +++ b/tests/ui/print-request/backend-has-zstd-unstable.rs @@ -0,0 +1,8 @@ +//! Check that `--print=backend-has-zstd` is unstable. +//! +//! That print value is intended for use by compiletest, and should probably +//! never be stabilized in this form. + +//@ compile-flags: --print=backend-has-zstd + +//~? ERROR: the `-Z unstable-options` flag must also be passed diff --git a/tests/ui/print-request/backend-has-zstd-unstable.stderr b/tests/ui/print-request/backend-has-zstd-unstable.stderr new file mode 100644 index 0000000000000..c7b5aa68d873c --- /dev/null +++ b/tests/ui/print-request/backend-has-zstd-unstable.stderr @@ -0,0 +1,2 @@ +error: the `-Z unstable-options` flag must also be passed to enable the `backend-has-zstd` print option + diff --git a/tests/ui/print-request/print-lints-help.stderr b/tests/ui/print-request/print-lints-help.stderr index 297a3aa79e1f3..d39c6326e318b 100644 --- a/tests/ui/print-request/print-lints-help.stderr +++ b/tests/ui/print-request/print-lints-help.stderr @@ -1,6 +1,6 @@ error: unknown print request: `lints` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: use `-Whelp` to print a list of lints = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information From f040a1a9152b6bd080692750183810cb0c37290b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Dec 2025 08:42:24 +0100 Subject: [PATCH 11/12] interpret: test SNaN handling of float min/max and update comments --- .../src/interpret/intrinsics.rs | 52 +++++++++++++------ .../src/interpret/intrinsics/simd.rs | 12 ++--- src/tools/miri/tests/pass/float.rs | 52 ++++++++----------- 3 files changed, 64 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index fa5041daa69e9..a7a3bbebed5f1 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -40,18 +40,20 @@ pub(crate) enum MinMax { /// In particular, `-0.0` is considered smaller than `+0.0` and /// if either input is NaN, the result is NaN. Minimum, - /// The IEEE-2008 `minNum` operation - see `f32::min` etc. + /// The IEEE-2008 `minNum` operation with the SNaN handling of the + /// IEEE-2019 `minimumNumber` operation - see `f32::min` etc. /// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic, - /// and if one argument is NaN, the other one is returned. - MinNum, + /// and if one argument is NaN (quiet or signaling), the other one is returned. + MinimumNumber, /// The IEEE-2019 `maximum` operation - see `f32::maximum` etc. /// In particular, `-0.0` is considered smaller than `+0.0` and /// if either input is NaN, the result is NaN. Maximum, - /// The IEEE-2008 `maxNum` operation - see `f32::max` etc. + /// The IEEE-2008 `maxNum` operation with the SNaN handling of the + /// IEEE-2019 `maximumNumber` operation - see `f32::max` etc. /// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic, - /// and if one argument is NaN, the other one is returned. - MaxNum, + /// and if one argument is NaN (quiet or signaling), the other one is returned. + MaximumNumber, } /// Directly returns an `Allocation` containing an absolute path representation of the given type. @@ -524,10 +526,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?; } - sym::minnumf16 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, - sym::minnumf32 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, - sym::minnumf64 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, - sym::minnumf128 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, + sym::minnumf16 => { + self.float_minmax_intrinsic::(args, MinMax::MinimumNumber, dest)? + } + sym::minnumf32 => { + self.float_minmax_intrinsic::(args, MinMax::MinimumNumber, dest)? + } + sym::minnumf64 => { + self.float_minmax_intrinsic::(args, MinMax::MinimumNumber, dest)? + } + sym::minnumf128 => { + self.float_minmax_intrinsic::(args, MinMax::MinimumNumber, dest)? + } sym::minimumf16 => self.float_minmax_intrinsic::(args, MinMax::Minimum, dest)?, sym::minimumf32 => { @@ -538,10 +548,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } sym::minimumf128 => self.float_minmax_intrinsic::(args, MinMax::Minimum, dest)?, - sym::maxnumf16 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, - sym::maxnumf32 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, - sym::maxnumf64 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, - sym::maxnumf128 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, + sym::maxnumf16 => { + self.float_minmax_intrinsic::(args, MinMax::MaximumNumber, dest)? + } + sym::maxnumf32 => { + self.float_minmax_intrinsic::(args, MinMax::MaximumNumber, dest)? + } + sym::maxnumf64 => { + self.float_minmax_intrinsic::(args, MinMax::MaximumNumber, dest)? + } + sym::maxnumf128 => { + self.float_minmax_intrinsic::(args, MinMax::MaximumNumber, dest)? + } sym::maximumf16 => self.float_minmax_intrinsic::(args, MinMax::Maximum, dest)?, sym::maximumf32 => { @@ -966,16 +984,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { { let a: F = a.to_float()?; let b: F = b.to_float()?; - let res = if matches!(op, MinMax::MinNum | MinMax::MaxNum) && a == b { + let res = if matches!(op, MinMax::MinimumNumber | MinMax::MaximumNumber) && a == b { // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`. // Let the machine decide which one to return. M::equal_float_min_max(self, a, b) } else { let result = match op { MinMax::Minimum => a.minimum(b), - MinMax::MinNum => a.min(b), + MinMax::MinimumNumber => a.min(b), MinMax::Maximum => a.maximum(b), - MinMax::MaxNum => a.max(b), + MinMax::MaximumNumber => a.max(b), }; self.adjust_nan(result, &[a, b]) }; diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs index bae423840ee1b..20de476831225 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -202,8 +202,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::simd_le => Op::MirOp(BinOp::Le), sym::simd_gt => Op::MirOp(BinOp::Gt), sym::simd_ge => Op::MirOp(BinOp::Ge), - sym::simd_fmax => Op::FMinMax(MinMax::MaxNum), - sym::simd_fmin => Op::FMinMax(MinMax::MinNum), + sym::simd_fmax => Op::FMinMax(MinMax::MaximumNumber), + sym::simd_fmin => Op::FMinMax(MinMax::MinimumNumber), sym::simd_saturating_add => Op::SaturatingOp(BinOp::Add), sym::simd_saturating_sub => Op::SaturatingOp(BinOp::Sub), sym::simd_arith_offset => Op::WrappingOffset, @@ -295,8 +295,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::simd_reduce_xor => Op::MirOp(BinOp::BitXor), sym::simd_reduce_any => Op::MirOpBool(BinOp::BitOr), sym::simd_reduce_all => Op::MirOpBool(BinOp::BitAnd), - sym::simd_reduce_max => Op::MinMax(MinMax::MaxNum), - sym::simd_reduce_min => Op::MinMax(MinMax::MinNum), + sym::simd_reduce_max => Op::MinMax(MinMax::MaximumNumber), + sym::simd_reduce_min => Op::MinMax(MinMax::MinimumNumber), _ => unreachable!(), }; @@ -320,8 +320,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } else { // Just boring integers, no NaNs to worry about. let mirop = match mmop { - MinMax::MinNum | MinMax::Minimum => BinOp::Le, - MinMax::MaxNum | MinMax::Maximum => BinOp::Ge, + MinMax::MinimumNumber | MinMax::Minimum => BinOp::Le, + MinMax::MaximumNumber | MinMax::Maximum => BinOp::Ge, }; if self.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? { res diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 7b23518d73dad..a74a66d5455a8 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -48,29 +48,15 @@ macro_rules! assert_approx_eq { }; } -/// From IEEE 754 a Signaling NaN for single precision has the following representation: -/// ``` -/// s | 1111 1111 | 0x..x -/// ```` -/// Were at least one `x` is a 1. -/// -/// This sNaN has the following representation and is used for testing purposes.: -/// ``` -/// 0 | 1111111 | 01..0 -/// ``` -const SNAN_F32: f32 = f32::from_bits(0x7fa00000); - -/// From IEEE 754 a Signaling NaN for double precision has the following representation: -/// ``` -/// s | 1111 1111 111 | 0x..x -/// ```` -/// Were at least one `x` is a 1. -/// -/// This sNaN has the following representation and is used for testing purposes.: -/// ``` -/// 0 | 1111 1111 111 | 01..0 -/// ``` -const SNAN_F64: f64 = f64::from_bits(0x7ff4000000000000); +/// We turn the quiet NaN f*::NAN into a signaling one by flipping the first (most significant) +/// two bits of the mantissa. For this we have to shift by `MANTISSA_DIGITS-3` because: +/// we subtract 1 as the actual mantissa is 1 bit smaller, and 2 more as that's the width +/// if the value we are shifting. +const F16_SNAN: f16 = f16::from_bits(f16::NAN.to_bits() ^ (0b11 << (f16::MANTISSA_DIGITS - 3))); +const F32_SNAN: f32 = f32::from_bits(f32::NAN.to_bits() ^ (0b11 << (f32::MANTISSA_DIGITS - 3))); +const F64_SNAN: f64 = f64::from_bits(f64::NAN.to_bits() ^ (0b11 << (f64::MANTISSA_DIGITS - 3))); +const F128_SNAN: f128 = + f128::from_bits(f128::NAN.to_bits() ^ (0b11 << (f128::MANTISSA_DIGITS - 3))); fn main() { basic(); @@ -757,6 +743,8 @@ fn ops() { assert_eq(f16::NAN.max(-9.0), -9.0); assert_eq((9.0_f16).min(f16::NAN), 9.0); assert_eq((-9.0_f16).max(f16::NAN), -9.0); + assert_eq(F16_SNAN.min(9.0), 9.0); + assert_eq((-9.0_f16).max(F16_SNAN), -9.0); // f32 min/max assert_eq((1.0 as f32).max(-1.0), 1.0); @@ -765,6 +753,8 @@ fn ops() { assert_eq(f32::NAN.max(-9.0), -9.0); assert_eq((9.0 as f32).min(f32::NAN), 9.0); assert_eq((-9.0 as f32).max(f32::NAN), -9.0); + assert_eq(F32_SNAN.min(9.0), 9.0); + assert_eq((-9.0_f32).max(F32_SNAN), -9.0); // f64 min/max assert_eq((1.0 as f64).max(-1.0), 1.0); @@ -773,6 +763,8 @@ fn ops() { assert_eq(f64::NAN.max(-9.0), -9.0); assert_eq((9.0 as f64).min(f64::NAN), 9.0); assert_eq((-9.0 as f64).max(f64::NAN), -9.0); + assert_eq(F64_SNAN.min(9.0), 9.0); + assert_eq((-9.0_f64).max(F64_SNAN), -9.0); // f128 min/max assert_eq((1.0_f128).max(-1.0), 1.0); @@ -781,6 +773,8 @@ fn ops() { assert_eq(f128::NAN.max(-9.0), -9.0); assert_eq((9.0_f128).min(f128::NAN), 9.0); assert_eq((-9.0_f128).max(f128::NAN), -9.0); + assert_eq(F128_SNAN.min(9.0), 9.0); + assert_eq((-9.0_f128).max(F128_SNAN), -9.0); // f16 copysign assert_eq(3.5_f16.copysign(0.42), 3.5_f16); @@ -1548,15 +1542,15 @@ fn test_non_determinism() { test_operations_f128(25., 18.); // SNaN^0 = (1 | NaN) - check_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan()); - check_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan()); + check_nondet(|| f32::powf(F32_SNAN, 0.0).is_nan()); + check_nondet(|| f64::powf(F64_SNAN, 0.0).is_nan()); // 1^SNaN = (1 | NaN) - check_nondet(|| f32::powf(1.0, SNAN_F32).is_nan()); - check_nondet(|| f64::powf(1.0, SNAN_F64).is_nan()); + check_nondet(|| f32::powf(1.0, F32_SNAN).is_nan()); + check_nondet(|| f64::powf(1.0, F64_SNAN).is_nan()); // same as powf (keep it consistent): // x^SNaN = (1 | NaN) - check_nondet(|| f32::powi(SNAN_F32, 0).is_nan()); - check_nondet(|| f64::powi(SNAN_F64, 0).is_nan()); + check_nondet(|| f32::powi(F32_SNAN, 0).is_nan()); + check_nondet(|| f64::powi(F64_SNAN, 0).is_nan()); } From 3e717121a1c96ed9717ebac84ecbeac021dabb21 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 2 Dec 2025 15:30:06 +0300 Subject: [PATCH 12/12] Generate error delegation body when delegation is not resolved --- compiler/rustc_ast_lowering/src/delegation.rs | 40 +++++++++++++++++-- .../ice-line-bounds-issue-148732.rs | 1 + .../ice-line-bounds-issue-148732.stderr | 18 +++++++-- .../ui/delegation/unused-import-ice-144594.rs | 13 ++++++ .../unused-import-ice-144594.stderr | 15 +++++++ 5 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 tests/ui/delegation/unused-import-ice-144594.rs create mode 100644 tests/ui/delegation/unused-import-ice-144594.stderr diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 9bfcd232221ba..e6e88eff2d5bc 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -96,7 +96,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let generics = self.lower_delegation_generics(span); DelegationResults { body_id, sig, ident, generics } } - Err(err) => self.generate_delegation_error(err, span), + Err(err) => self.generate_delegation_error(err, span, delegation), } } @@ -404,6 +404,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, err: ErrorGuaranteed, span: Span, + delegation: &Delegation, ) -> DelegationResults<'hir> { let generics = self.lower_delegation_generics(span); @@ -418,8 +419,41 @@ impl<'hir> LoweringContext<'_, 'hir> { let header = self.generate_header_error(); let sig = hir::FnSig { decl, header, span }; - let ident = Ident::dummy(); - let body_id = self.lower_body(|this| (&[], this.mk_expr(hir::ExprKind::Err(err), span))); + let ident = self.lower_ident(delegation.ident); + + let body_id = self.lower_body(|this| { + let body_expr = match delegation.body.as_ref() { + Some(box block) => { + // Generates a block when we failed to resolve delegation, where a target expression is its only statement, + // thus there will be no ICEs on further stages of analysis (see #144594) + + // As we generate a void function we want to convert target expression to statement to avoid additional + // errors, such as mismatched return type + let stmts = this.arena.alloc_from_iter([hir::Stmt { + hir_id: this.next_id(), + kind: rustc_hir::StmtKind::Semi( + this.arena.alloc(this.lower_target_expr(block)), + ), + span, + }]); + + let block = this.arena.alloc(hir::Block { + stmts, + expr: None, + hir_id: this.next_id(), + rules: hir::BlockCheckMode::DefaultBlock, + span, + targeted_by_break: false, + }); + + hir::ExprKind::Block(block, None) + } + None => hir::ExprKind::Err(err), + }; + + (&[], this.mk_expr(body_expr, span)) + }); + DelegationResults { ident, generics, body_id, sig } } diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.rs b/tests/ui/delegation/ice-line-bounds-issue-148732.rs index 699e7d86f2581..e44c784760216 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.rs +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.rs @@ -3,6 +3,7 @@ reuse a as b { //~| ERROR functions delegation is not yet fully implemented dbg!(b); //~^ ERROR missing lifetime specifier + //~| ERROR `fn() {b}` doesn't implement `Debug` } fn main() {} diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr index c65b1560818d7..1f43ec335448b 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr @@ -20,7 +20,7 @@ LL | / reuse a as b { LL | | LL | | LL | | dbg!(b); -LL | | +... | LL | | } | |_^ | @@ -28,7 +28,19 @@ LL | | } = help: add `#![feature(fn_delegation)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 3 previous errors +error[E0277]: `fn() {b}` doesn't implement `Debug` + --> $DIR/ice-line-bounds-issue-148732.rs:4:5 + | +LL | reuse a as b { + | - consider calling this function +... +LL | dbg!(b); + | ^^^^^^^ the trait `Debug` is not implemented for fn item `fn() {b}` + | + = help: use parentheses to call this function: `b()` + = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0106, E0425, E0658. +Some errors have detailed explanations: E0106, E0277, E0425, E0658. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/delegation/unused-import-ice-144594.rs b/tests/ui/delegation/unused-import-ice-144594.rs new file mode 100644 index 0000000000000..1d064a4c978f1 --- /dev/null +++ b/tests/ui/delegation/unused-import-ice-144594.rs @@ -0,0 +1,13 @@ +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +reuse a as b { + //~^ ERROR cannot find function `a` in this scope [E0425] + || { + use std::ops::Add; + x.add + //~^ ERROR cannot find value `x` in this scope [E0425] + } +} + +fn main() {} diff --git a/tests/ui/delegation/unused-import-ice-144594.stderr b/tests/ui/delegation/unused-import-ice-144594.stderr new file mode 100644 index 0000000000000..1939380235eed --- /dev/null +++ b/tests/ui/delegation/unused-import-ice-144594.stderr @@ -0,0 +1,15 @@ +error[E0425]: cannot find function `a` in this scope + --> $DIR/unused-import-ice-144594.rs:4:7 + | +LL | reuse a as b { + | ^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/unused-import-ice-144594.rs:8:9 + | +LL | x.add + | ^ not found in this scope + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`.