From a4d969b30e5422f0787492b67283aa389f858187 Mon Sep 17 00:00:00 2001
From: Markus Reiter <me@reitermark.us>
Date: Sun, 18 Feb 2024 12:32:36 +0100
Subject: [PATCH 01/12] Refactor trait implementations in `core::convert::num`.

---
 library/core/src/convert/num.rs | 692 +++++++++++++++-----------------
 1 file changed, 325 insertions(+), 367 deletions(-)

diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs
index 08dc8f48dfedc..46a9006c14665 100644
--- a/library/core/src/convert/num.rs
+++ b/library/core/src/convert/num.rs
@@ -19,7 +19,7 @@ pub trait FloatToInt<Int>: private::Sealed + Sized {
 }
 
 macro_rules! impl_float_to_int {
-    ( $Float: ident => $( $Int: ident )+ ) => {
+    ($Float:ty => $($Int:ty),+) => {
         #[unstable(feature = "convert_float_to_int", issue = "67057")]
         impl private::Sealed for $Float {}
         $(
@@ -35,14 +35,38 @@ macro_rules! impl_float_to_int {
     }
 }
 
-impl_float_to_int!(f32 => u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);
-impl_float_to_int!(f64 => u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);
+impl_float_to_int!(f32 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
+impl_float_to_int!(f64 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
 
 // Conversion traits for primitive integer and float types
 // Conversions T -> T are covered by a blanket impl and therefore excluded
 // Some conversions from and to usize/isize are not implemented due to portability concerns
 macro_rules! impl_from {
-    ($Small: ty, $Large: ty, #[$attr:meta], $doc: expr) => {
+    (bool => $Int:ty $(,)?) => {
+        impl_from!(
+            bool => $Int,
+            #[stable(feature = "from_bool", since = "1.28.0")],
+            concat!(
+                "Converts a [`bool`] to [`", stringify!($Int), "`] losslessly.\n",
+                "The resulting value is `0` for `false` and `1` for `true` values.\n",
+                "\n",
+                "# Examples\n",
+                "\n",
+                "```\n",
+                "assert_eq!(", stringify!($Int), "::from(true), 1);\n",
+                "assert_eq!(", stringify!($Int), "::from(false), 0);\n",
+                "```\n",
+            ),
+        );
+    };
+    ($Small:ty => $Large:ty, #[$attr:meta] $(,)?) => {
+        impl_from!(
+            $Small => $Large,
+            #[$attr],
+            concat!("Converts [`", stringify!($Small), "`] to [`", stringify!($Large), "`] losslessly."),
+        );
+    };
+    ($Small:ty => $Large:ty, #[$attr:meta], $doc:expr $(,)?) => {
         #[$attr]
         impl From<$Small> for $Large {
             // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
@@ -54,91 +78,66 @@ macro_rules! impl_from {
             }
         }
     };
-    ($Small: ty, $Large: ty, #[$attr:meta]) => {
-        impl_from!($Small,
-                   $Large,
-                   #[$attr],
-                   concat!("Converts `",
-                           stringify!($Small),
-                           "` to `",
-                           stringify!($Large),
-                           "` losslessly."));
-    }
 }
 
-macro_rules! impl_from_bool {
-    ($target: ty, #[$attr:meta]) => {
-        impl_from!(bool, $target, #[$attr], concat!("Converts a `bool` to a `",
-            stringify!($target), "`. The resulting value is `0` for `false` and `1` for `true`
-values.
-
-# Examples
-
-```
-assert_eq!(", stringify!($target), "::from(true), 1);
-assert_eq!(", stringify!($target), "::from(false), 0);
-```"));
-    };
-}
-
-// Bool -> Any
-impl_from_bool! { u8, #[stable(feature = "from_bool", since = "1.28.0")] }
-impl_from_bool! { u16, #[stable(feature = "from_bool", since = "1.28.0")] }
-impl_from_bool! { u32, #[stable(feature = "from_bool", since = "1.28.0")] }
-impl_from_bool! { u64, #[stable(feature = "from_bool", since = "1.28.0")] }
-impl_from_bool! { u128, #[stable(feature = "from_bool", since = "1.28.0")] }
-impl_from_bool! { usize, #[stable(feature = "from_bool", since = "1.28.0")] }
-impl_from_bool! { i8, #[stable(feature = "from_bool", since = "1.28.0")] }
-impl_from_bool! { i16, #[stable(feature = "from_bool", since = "1.28.0")] }
-impl_from_bool! { i32, #[stable(feature = "from_bool", since = "1.28.0")] }
-impl_from_bool! { i64, #[stable(feature = "from_bool", since = "1.28.0")] }
-impl_from_bool! { i128, #[stable(feature = "from_bool", since = "1.28.0")] }
-impl_from_bool! { isize, #[stable(feature = "from_bool", since = "1.28.0")] }
-
-// Unsigned -> Unsigned
-impl_from! { u8, u16, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u8, u32, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u8, u64, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u8, u128, #[stable(feature = "i128", since = "1.26.0")] }
-impl_from! { u8, usize, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u16, u32, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u16, u64, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u16, u128, #[stable(feature = "i128", since = "1.26.0")] }
-impl_from! { u32, u64, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u32, u128, #[stable(feature = "i128", since = "1.26.0")] }
-impl_from! { u64, u128, #[stable(feature = "i128", since = "1.26.0")] }
-
-// Signed -> Signed
-impl_from! { i8, i16, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { i8, i32, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { i8, i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { i8, i128, #[stable(feature = "i128", since = "1.26.0")] }
-impl_from! { i8, isize, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { i16, i32, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { i16, i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { i16, i128, #[stable(feature = "i128", since = "1.26.0")] }
-impl_from! { i32, i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { i32, i128, #[stable(feature = "i128", since = "1.26.0")] }
-impl_from! { i64, i128, #[stable(feature = "i128", since = "1.26.0")] }
-
-// Unsigned -> Signed
-impl_from! { u8, i16, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u8, i32, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u8, i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u8, i128, #[stable(feature = "i128", since = "1.26.0")] }
-impl_from! { u16, i32, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u16, i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u16, i128, #[stable(feature = "i128", since = "1.26.0")] }
-impl_from! { u32, i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")] }
-impl_from! { u32, i128, #[stable(feature = "i128", since = "1.26.0")] }
-impl_from! { u64, i128, #[stable(feature = "i128", since = "1.26.0")] }
+// boolean -> integer
+impl_from!(bool => u8);
+impl_from!(bool => u16);
+impl_from!(bool => u32);
+impl_from!(bool => u64);
+impl_from!(bool => u128);
+impl_from!(bool => usize);
+impl_from!(bool => i8);
+impl_from!(bool => i16);
+impl_from!(bool => i32);
+impl_from!(bool => i64);
+impl_from!(bool => i128);
+impl_from!(bool => isize);
+
+// unsigned integer -> unsigned integer
+impl_from!(u8 => u16, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u8 => u32, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u8 => u64, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u8 => u128, #[stable(feature = "i128", since = "1.26.0")]);
+impl_from!(u8 => usize, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u16 => u32, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u16 => u64, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u16 => u128, #[stable(feature = "i128", since = "1.26.0")]);
+impl_from!(u32 => u64, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u32 => u128, #[stable(feature = "i128", since = "1.26.0")]);
+impl_from!(u64 => u128, #[stable(feature = "i128", since = "1.26.0")]);
+
+// signed integer -> signed integer
+impl_from!(i8 => i16, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(i8 => i32, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(i8 => i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(i8 => i128, #[stable(feature = "i128", since = "1.26.0")]);
+impl_from!(i8 => isize, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(i16 => i32, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(i16 => i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(i16 => i128, #[stable(feature = "i128", since = "1.26.0")]);
+impl_from!(i32 => i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(i32 => i128, #[stable(feature = "i128", since = "1.26.0")]);
+impl_from!(i64 => i128, #[stable(feature = "i128", since = "1.26.0")]);
+
+// unsigned integer -> signed integer
+impl_from!(u8 => i16, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u8 => i32, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u8 => i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u8 => i128, #[stable(feature = "i128", since = "1.26.0")]);
+impl_from!(u16 => i32, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u16 => i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u16 => i128, #[stable(feature = "i128", since = "1.26.0")]);
+impl_from!(u32 => i64, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);
+impl_from!(u32 => i128, #[stable(feature = "i128", since = "1.26.0")]);
+impl_from!(u64 => i128, #[stable(feature = "i128", since = "1.26.0")]);
 
 // The C99 standard defines bounds on INTPTR_MIN, INTPTR_MAX, and UINTPTR_MAX
 // which imply that pointer-sized integers must be at least 16 bits:
 // https://port70.net/~nsz/c/c99/n1256.html#7.18.2.4
-impl_from! { u16, usize, #[stable(feature = "lossless_iusize_conv", since = "1.26.0")] }
-impl_from! { u8, isize, #[stable(feature = "lossless_iusize_conv", since = "1.26.0")] }
-impl_from! { i16, isize, #[stable(feature = "lossless_iusize_conv", since = "1.26.0")] }
+impl_from!(u16 => usize, #[stable(feature = "lossless_iusize_conv", since = "1.26.0")]);
+impl_from!(u8 => isize, #[stable(feature = "lossless_iusize_conv", since = "1.26.0")]);
+impl_from!(i16 => isize, #[stable(feature = "lossless_iusize_conv", since = "1.26.0")]);
 
 // RISC-V defines the possibility of a 128-bit address space (RV128).
 
@@ -150,66 +149,54 @@ impl_from! { i16, isize, #[stable(feature = "lossless_iusize_conv", since = "1.2
 // they fit in the significand, which is 24 bits in f32 and 53 bits in f64.
 // Lossy float conversions are not implemented at this time.
 
-// Signed -> Float
-impl_from! { i8, f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
-impl_from! { i8, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
-impl_from! { i16, f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
-impl_from! { i16, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
-impl_from! { i32, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
-
-// Unsigned -> Float
-impl_from! { u8, f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
-impl_from! { u8, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
-impl_from! { u16, f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
-impl_from! { u16, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
-impl_from! { u32, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
-
-// Float -> Float
-impl_from! { f32, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
-
-// bool -> Float
-#[stable(feature = "float_from_bool", since = "1.68.0")]
-impl From<bool> for f32 {
-    /// Converts `bool` to `f32` losslessly. The resulting value is positive
-    /// `0.0` for `false` and `1.0` for `true` values.
-    ///
-    /// # Examples
-    /// ```
-    /// let x: f32 = false.into();
-    /// assert_eq!(x, 0.0);
-    /// assert!(x.is_sign_positive());
-    ///
-    /// let y: f32 = true.into();
-    /// assert_eq!(y, 1.0);
-    /// ```
-    #[inline]
-    fn from(small: bool) -> Self {
-        small as u8 as Self
-    }
-}
-#[stable(feature = "float_from_bool", since = "1.68.0")]
-impl From<bool> for f64 {
-    /// Converts `bool` to `f64` losslessly. The resulting value is positive
-    /// `0.0` for `false` and `1.0` for `true` values.
-    ///
-    /// # Examples
-    /// ```
-    /// let x: f64 = false.into();
-    /// assert_eq!(x, 0.0);
-    /// assert!(x.is_sign_positive());
-    ///
-    /// let y: f64 = true.into();
-    /// assert_eq!(y, 1.0);
-    /// ```
-    #[inline]
-    fn from(small: bool) -> Self {
-        small as u8 as Self
-    }
+// signed integer -> float
+impl_from!(i8 => f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
+impl_from!(i8 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
+impl_from!(i16 => f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
+impl_from!(i16 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
+impl_from!(i32 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
+
+// unsigned integer -> float
+impl_from!(u8 => f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
+impl_from!(u8 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
+impl_from!(u16 => f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
+impl_from!(u16 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
+impl_from!(u32 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
+
+// float -> float
+impl_from!(f32 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
+
+macro_rules! impl_float_from_bool {
+    ($float:ty) => {
+        #[stable(feature = "float_from_bool", since = "1.68.0")]
+        impl From<bool> for $float {
+            #[doc = concat!("Converts a [`bool`] to [`", stringify!($float),"`] losslessly.")]
+            /// The resulting value is positive `0.0` for `false` and `1.0` for `true` values.
+            ///
+            /// # Examples
+            /// ```
+            #[doc = concat!("let x: ", stringify!($float)," = false.into();")]
+            /// assert_eq!(x, 0.0);
+            /// assert!(x.is_sign_positive());
+            ///
+            #[doc = concat!("let y: ", stringify!($float)," = true.into();")]
+            /// assert_eq!(y, 1.0);
+            /// ```
+            #[inline]
+            fn from(small: bool) -> Self {
+                small as u8 as Self
+            }
+        }
+    };
 }
 
+// boolean -> float
+impl_float_from_bool!(f32);
+impl_float_from_bool!(f64);
+
 // no possible bounds violation
-macro_rules! try_from_unbounded {
-    ($source:ty, $($target:ty),*) => {$(
+macro_rules! impl_try_from_unbounded {
+    ($source:ty => $($target:ty),+) => {$(
         #[stable(feature = "try_from", since = "1.34.0")]
         impl TryFrom<$source> for $target {
             type Error = TryFromIntError;
@@ -226,8 +213,8 @@ macro_rules! try_from_unbounded {
 }
 
 // only negative bounds
-macro_rules! try_from_lower_bounded {
-    ($source:ty, $($target:ty),*) => {$(
+macro_rules! impl_try_from_lower_bounded {
+    ($source:ty => $($target:ty),+) => {$(
         #[stable(feature = "try_from", since = "1.34.0")]
         impl TryFrom<$source> for $target {
             type Error = TryFromIntError;
@@ -248,8 +235,8 @@ macro_rules! try_from_lower_bounded {
 }
 
 // unsigned to signed (only positive bound)
-macro_rules! try_from_upper_bounded {
-    ($source:ty, $($target:ty),*) => {$(
+macro_rules! impl_try_from_upper_bounded {
+    ($source:ty => $($target:ty),+) => {$(
         #[stable(feature = "try_from", since = "1.34.0")]
         impl TryFrom<$source> for $target {
             type Error = TryFromIntError;
@@ -270,8 +257,8 @@ macro_rules! try_from_upper_bounded {
 }
 
 // all other cases
-macro_rules! try_from_both_bounded {
-    ($source:ty, $($target:ty),*) => {$(
+macro_rules! impl_try_from_both_bounded {
+    ($source:ty => $($target:ty),+) => {$(
         #[stable(feature = "try_from", since = "1.34.0")]
         impl TryFrom<$source> for $target {
             type Error = TryFromIntError;
@@ -294,65 +281,66 @@ macro_rules! try_from_both_bounded {
 }
 
 macro_rules! rev {
-    ($mac:ident, $source:ty, $($target:ty),*) => {$(
-        $mac!($target, $source);
+    ($mac:ident, $source:ty => $($target:ty),+) => {$(
+        $mac!($target => $source);
     )*}
 }
 
-// intra-sign conversions
-try_from_upper_bounded!(u16, u8);
-try_from_upper_bounded!(u32, u16, u8);
-try_from_upper_bounded!(u64, u32, u16, u8);
-try_from_upper_bounded!(u128, u64, u32, u16, u8);
-
-try_from_both_bounded!(i16, i8);
-try_from_both_bounded!(i32, i16, i8);
-try_from_both_bounded!(i64, i32, i16, i8);
-try_from_both_bounded!(i128, i64, i32, i16, i8);
-
-// unsigned-to-signed
-try_from_upper_bounded!(u8, i8);
-try_from_upper_bounded!(u16, i8, i16);
-try_from_upper_bounded!(u32, i8, i16, i32);
-try_from_upper_bounded!(u64, i8, i16, i32, i64);
-try_from_upper_bounded!(u128, i8, i16, i32, i64, i128);
-
-// signed-to-unsigned
-try_from_lower_bounded!(i8, u8, u16, u32, u64, u128);
-try_from_lower_bounded!(i16, u16, u32, u64, u128);
-try_from_lower_bounded!(i32, u32, u64, u128);
-try_from_lower_bounded!(i64, u64, u128);
-try_from_lower_bounded!(i128, u128);
-try_from_both_bounded!(i16, u8);
-try_from_both_bounded!(i32, u16, u8);
-try_from_both_bounded!(i64, u32, u16, u8);
-try_from_both_bounded!(i128, u64, u32, u16, u8);
+// unsigned integer -> unsigned integer
+impl_try_from_upper_bounded!(u16 => u8);
+impl_try_from_upper_bounded!(u32 => u8, u16);
+impl_try_from_upper_bounded!(u64 => u8, u16, u32);
+impl_try_from_upper_bounded!(u128 => u8, u16, u32, u64);
+
+// signed integer -> signed integer
+impl_try_from_both_bounded!(i16 => i8);
+impl_try_from_both_bounded!(i32 => i8, i16);
+impl_try_from_both_bounded!(i64 => i8, i16, i32);
+impl_try_from_both_bounded!(i128 => i8, i16, i32, i64);
+
+// unsigned integer -> signed integer
+impl_try_from_upper_bounded!(u8 => i8);
+impl_try_from_upper_bounded!(u16 => i8, i16);
+impl_try_from_upper_bounded!(u32 => i8, i16, i32);
+impl_try_from_upper_bounded!(u64 => i8, i16, i32, i64);
+impl_try_from_upper_bounded!(u128 => i8, i16, i32, i64, i128);
+
+// signed integer -> unsigned integer
+impl_try_from_lower_bounded!(i8 => u8, u16, u32, u64, u128);
+impl_try_from_both_bounded!(i16 => u8);
+impl_try_from_lower_bounded!(i16 => u16, u32, u64, u128);
+impl_try_from_both_bounded!(i32 => u8, u16);
+impl_try_from_lower_bounded!(i32 => u32, u64, u128);
+impl_try_from_both_bounded!(i64 => u8, u16, u32);
+impl_try_from_lower_bounded!(i64 => u64, u128);
+impl_try_from_both_bounded!(i128 => u8, u16, u32, u64);
+impl_try_from_lower_bounded!(i128 => u128);
 
 // usize/isize
-try_from_upper_bounded!(usize, isize);
-try_from_lower_bounded!(isize, usize);
+impl_try_from_upper_bounded!(usize => isize);
+impl_try_from_lower_bounded!(isize => usize);
 
 #[cfg(target_pointer_width = "16")]
 mod ptr_try_from_impls {
     use super::TryFromIntError;
     use crate::convert::TryFrom;
 
-    try_from_upper_bounded!(usize, u8);
-    try_from_unbounded!(usize, u16, u32, u64, u128);
-    try_from_upper_bounded!(usize, i8, i16);
-    try_from_unbounded!(usize, i32, i64, i128);
+    impl_try_from_upper_bounded!(usize => u8);
+    impl_try_from_unbounded!(usize => u16, u32, u64, u128);
+    impl_try_from_upper_bounded!(usize => i8, i16);
+    impl_try_from_unbounded!(usize => i32, i64, i128);
 
-    try_from_both_bounded!(isize, u8);
-    try_from_lower_bounded!(isize, u16, u32, u64, u128);
-    try_from_both_bounded!(isize, i8);
-    try_from_unbounded!(isize, i16, i32, i64, i128);
+    impl_try_from_both_bounded!(isize => u8);
+    impl_try_from_lower_bounded!(isize => u16, u32, u64, u128);
+    impl_try_from_both_bounded!(isize => i8);
+    impl_try_from_unbounded!(isize => i16, i32, i64, i128);
 
-    rev!(try_from_upper_bounded, usize, u32, u64, u128);
-    rev!(try_from_lower_bounded, usize, i8, i16);
-    rev!(try_from_both_bounded, usize, i32, i64, i128);
+    rev!(impl_try_from_upper_bounded, usize => u32, u64, u128);
+    rev!(impl_try_from_lower_bounded, usize => i8, i16);
+    rev!(impl_try_from_both_bounded, usize => i32, i64, i128);
 
-    rev!(try_from_upper_bounded, isize, u16, u32, u64, u128);
-    rev!(try_from_both_bounded, isize, i32, i64, i128);
+    rev!(impl_try_from_upper_bounded, isize => u16, u32, u64, u128);
+    rev!(impl_try_from_both_bounded, isize => i32, i64, i128);
 }
 
 #[cfg(target_pointer_width = "32")]
@@ -360,25 +348,25 @@ mod ptr_try_from_impls {
     use super::TryFromIntError;
     use crate::convert::TryFrom;
 
-    try_from_upper_bounded!(usize, u8, u16);
-    try_from_unbounded!(usize, u32, u64, u128);
-    try_from_upper_bounded!(usize, i8, i16, i32);
-    try_from_unbounded!(usize, i64, i128);
-
-    try_from_both_bounded!(isize, u8, u16);
-    try_from_lower_bounded!(isize, u32, u64, u128);
-    try_from_both_bounded!(isize, i8, i16);
-    try_from_unbounded!(isize, i32, i64, i128);
-
-    rev!(try_from_unbounded, usize, u32);
-    rev!(try_from_upper_bounded, usize, u64, u128);
-    rev!(try_from_lower_bounded, usize, i8, i16, i32);
-    rev!(try_from_both_bounded, usize, i64, i128);
-
-    rev!(try_from_unbounded, isize, u16);
-    rev!(try_from_upper_bounded, isize, u32, u64, u128);
-    rev!(try_from_unbounded, isize, i32);
-    rev!(try_from_both_bounded, isize, i64, i128);
+    impl_try_from_upper_bounded!(usize => u8, u16);
+    impl_try_from_unbounded!(usize => u32, u64, u128);
+    impl_try_from_upper_bounded!(usize => i8, i16, i32);
+    impl_try_from_unbounded!(usize => i64, i128);
+
+    impl_try_from_both_bounded!(isize => u8, u16);
+    impl_try_from_lower_bounded!(isize => u32, u64, u128);
+    impl_try_from_both_bounded!(isize => i8, i16);
+    impl_try_from_unbounded!(isize => i32, i64, i128);
+
+    rev!(impl_try_from_unbounded, usize => u32);
+    rev!(impl_try_from_upper_bounded, usize => u64, u128);
+    rev!(impl_try_from_lower_bounded, usize => i8, i16, i32);
+    rev!(impl_try_from_both_bounded, usize => i64, i128);
+
+    rev!(impl_try_from_unbounded, isize => u16);
+    rev!(impl_try_from_upper_bounded, isize => u32, u64, u128);
+    rev!(impl_try_from_unbounded, isize => i32);
+    rev!(impl_try_from_both_bounded, isize => i64, i128);
 }
 
 #[cfg(target_pointer_width = "64")]
@@ -386,195 +374,165 @@ mod ptr_try_from_impls {
     use super::TryFromIntError;
     use crate::convert::TryFrom;
 
-    try_from_upper_bounded!(usize, u8, u16, u32);
-    try_from_unbounded!(usize, u64, u128);
-    try_from_upper_bounded!(usize, i8, i16, i32, i64);
-    try_from_unbounded!(usize, i128);
-
-    try_from_both_bounded!(isize, u8, u16, u32);
-    try_from_lower_bounded!(isize, u64, u128);
-    try_from_both_bounded!(isize, i8, i16, i32);
-    try_from_unbounded!(isize, i64, i128);
-
-    rev!(try_from_unbounded, usize, u32, u64);
-    rev!(try_from_upper_bounded, usize, u128);
-    rev!(try_from_lower_bounded, usize, i8, i16, i32, i64);
-    rev!(try_from_both_bounded, usize, i128);
-
-    rev!(try_from_unbounded, isize, u16, u32);
-    rev!(try_from_upper_bounded, isize, u64, u128);
-    rev!(try_from_unbounded, isize, i32, i64);
-    rev!(try_from_both_bounded, isize, i128);
+    impl_try_from_upper_bounded!(usize => u8, u16, u32);
+    impl_try_from_unbounded!(usize => u64, u128);
+    impl_try_from_upper_bounded!(usize => i8, i16, i32, i64);
+    impl_try_from_unbounded!(usize => i128);
+
+    impl_try_from_both_bounded!(isize => u8, u16, u32);
+    impl_try_from_lower_bounded!(isize => u64, u128);
+    impl_try_from_both_bounded!(isize => i8, i16, i32);
+    impl_try_from_unbounded!(isize => i64, i128);
+
+    rev!(impl_try_from_unbounded, usize => u32, u64);
+    rev!(impl_try_from_upper_bounded, usize => u128);
+    rev!(impl_try_from_lower_bounded, usize => i8, i16, i32, i64);
+    rev!(impl_try_from_both_bounded, usize => i128);
+
+    rev!(impl_try_from_unbounded, isize => u16, u32);
+    rev!(impl_try_from_upper_bounded, isize => u64, u128);
+    rev!(impl_try_from_unbounded, isize => i32, i64);
+    rev!(impl_try_from_both_bounded, isize => i128);
 }
 
 // Conversion traits for non-zero integer types
-use crate::num::NonZeroI128;
-use crate::num::NonZeroI16;
-use crate::num::NonZeroI32;
-use crate::num::NonZeroI64;
-use crate::num::NonZeroI8;
-use crate::num::NonZeroIsize;
-use crate::num::NonZeroU128;
-use crate::num::NonZeroU16;
-use crate::num::NonZeroU32;
-use crate::num::NonZeroU64;
-use crate::num::NonZeroU8;
-use crate::num::NonZeroUsize;
-
-macro_rules! nzint_impl_from {
-    ($Small: ty, $Large: ty, #[$attr:meta], $doc: expr) => {
-        #[$attr]
-        impl From<$Small> for $Large {
+use crate::num::NonZero;
+
+macro_rules! impl_nonzero_int_from_nonzero_int {
+    ($Small:ty => $Large:ty) => {
+        #[stable(feature = "nz_int_conv", since = "1.41.0")]
+        impl From<NonZero<$Small>> for NonZero<$Large> {
             // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
             // Rustdocs on functions do not.
-            #[doc = $doc]
+            #[doc = concat!("Converts <code>[NonZero]\\<[", stringify!($Small), "]></code> ")]
+            #[doc = concat!("to <code>[NonZero]\\<[", stringify!($Large), "]></code> losslessly.")]
             #[inline]
-            fn from(small: $Small) -> Self {
+            fn from(small: NonZero<$Small>) -> Self {
                 // SAFETY: input type guarantees the value is non-zero
-                unsafe {
-                    Self::new_unchecked(From::from(small.get()))
-                }
+                unsafe { Self::new_unchecked(From::from(small.get())) }
             }
         }
     };
-    ($Small: ty, $Large: ty, #[$attr:meta]) => {
-        nzint_impl_from!($Small,
-                   $Large,
-                   #[$attr],
-                   concat!("Converts `",
-                           stringify!($Small),
-                           "` to `",
-                           stringify!($Large),
-                           "` losslessly."));
-    }
 }
 
-// Non-zero Unsigned -> Non-zero Unsigned
-nzint_impl_from! { NonZeroU8, NonZeroU16, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU8, NonZeroU32, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU8, NonZeroU64, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU8, NonZeroU128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU8, NonZeroUsize, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU16, NonZeroU32, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU16, NonZeroU64, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU16, NonZeroU128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU16, NonZeroUsize, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU32, NonZeroU64, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU32, NonZeroU128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU64, NonZeroU128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-
-// Non-zero Signed -> Non-zero Signed
-nzint_impl_from! { NonZeroI8, NonZeroI16, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroI8, NonZeroI32, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroI8, NonZeroI64, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroI8, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroI8, NonZeroIsize, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroI16, NonZeroI32, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroI16, NonZeroI64, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroI16, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroI16, NonZeroIsize, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroI32, NonZeroI64, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroI32, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroI64, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-
-// NonZero UnSigned -> Non-zero Signed
-nzint_impl_from! { NonZeroU8, NonZeroI16, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU8, NonZeroI32, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU8, NonZeroI64, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU8, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU8, NonZeroIsize, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU16, NonZeroI32, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU16, NonZeroI64, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU16, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU32, NonZeroI64, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU32, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-nzint_impl_from! { NonZeroU64, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
-
-macro_rules! nzint_impl_try_from_int {
-    ($Int: ty, $NonZeroInt: ty, #[$attr:meta], $doc: expr) => {
-        #[$attr]
-        impl TryFrom<$Int> for $NonZeroInt {
+// non-zero unsigned integer -> non-zero unsigned integer
+impl_nonzero_int_from_nonzero_int!(u8 => u16);
+impl_nonzero_int_from_nonzero_int!(u8 => u32);
+impl_nonzero_int_from_nonzero_int!(u8 => u64);
+impl_nonzero_int_from_nonzero_int!(u8 => u128);
+impl_nonzero_int_from_nonzero_int!(u8 => usize);
+impl_nonzero_int_from_nonzero_int!(u16 => u32);
+impl_nonzero_int_from_nonzero_int!(u16 => u64);
+impl_nonzero_int_from_nonzero_int!(u16 => u128);
+impl_nonzero_int_from_nonzero_int!(u16 => usize);
+impl_nonzero_int_from_nonzero_int!(u32 => u64);
+impl_nonzero_int_from_nonzero_int!(u32 => u128);
+impl_nonzero_int_from_nonzero_int!(u64 => u128);
+
+// non-zero signed integer -> non-zero signed integer
+impl_nonzero_int_from_nonzero_int!(i8 => i16);
+impl_nonzero_int_from_nonzero_int!(i8 => i32);
+impl_nonzero_int_from_nonzero_int!(i8 => i64);
+impl_nonzero_int_from_nonzero_int!(i8 => i128);
+impl_nonzero_int_from_nonzero_int!(i8 => isize);
+impl_nonzero_int_from_nonzero_int!(i16 => i32);
+impl_nonzero_int_from_nonzero_int!(i16 => i64);
+impl_nonzero_int_from_nonzero_int!(i16 => i128);
+impl_nonzero_int_from_nonzero_int!(i16 => isize);
+impl_nonzero_int_from_nonzero_int!(i32 => i64);
+impl_nonzero_int_from_nonzero_int!(i32 => i128);
+impl_nonzero_int_from_nonzero_int!(i64 => i128);
+
+// non-zero unsigned -> non-zero signed integer
+impl_nonzero_int_from_nonzero_int!(u8 => i16);
+impl_nonzero_int_from_nonzero_int!(u8 => i32);
+impl_nonzero_int_from_nonzero_int!(u8 => i64);
+impl_nonzero_int_from_nonzero_int!(u8 => i128);
+impl_nonzero_int_from_nonzero_int!(u8 => isize);
+impl_nonzero_int_from_nonzero_int!(u16 => i32);
+impl_nonzero_int_from_nonzero_int!(u16 => i64);
+impl_nonzero_int_from_nonzero_int!(u16 => i128);
+impl_nonzero_int_from_nonzero_int!(u32 => i64);
+impl_nonzero_int_from_nonzero_int!(u32 => i128);
+impl_nonzero_int_from_nonzero_int!(u64 => i128);
+
+macro_rules! impl_nonzero_int_try_from_int {
+    ($Int:ty) => {
+        #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")]
+        impl TryFrom<$Int> for NonZero<$Int> {
             type Error = TryFromIntError;
 
             // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
             // Rustdocs on functions do not.
-            #[doc = $doc]
+            #[doc = concat!("Attempts to convert [`", stringify!($Int), "`] ")]
+            #[doc = concat!("to <code>[NonZero]\\<[", stringify!($Int), "]></code>.")]
             #[inline]
             fn try_from(value: $Int) -> Result<Self, Self::Error> {
                 Self::new(value).ok_or(TryFromIntError(()))
             }
         }
     };
-    ($Int: ty, $NonZeroInt: ty, #[$attr:meta]) => {
-        nzint_impl_try_from_int!($Int,
-                                 $NonZeroInt,
-                                 #[$attr],
-                                 concat!("Attempts to convert `",
-                                         stringify!($Int),
-                                         "` to `",
-                                         stringify!($NonZeroInt),
-                                         "`."));
-    }
 }
 
-// Int -> Non-zero Int
-nzint_impl_try_from_int! { u8, NonZeroU8, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-nzint_impl_try_from_int! { u16, NonZeroU16, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-nzint_impl_try_from_int! { u32, NonZeroU32, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-nzint_impl_try_from_int! { u64, NonZeroU64, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-nzint_impl_try_from_int! { u128, NonZeroU128, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-nzint_impl_try_from_int! { usize, NonZeroUsize, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-nzint_impl_try_from_int! { i8, NonZeroI8, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-nzint_impl_try_from_int! { i16, NonZeroI16, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-nzint_impl_try_from_int! { i32, NonZeroI32, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-nzint_impl_try_from_int! { i64, NonZeroI64, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-nzint_impl_try_from_int! { i128, NonZeroI128, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-nzint_impl_try_from_int! { isize, NonZeroIsize, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
-
-macro_rules! nzint_impl_try_from_nzint {
-    ($From:ty => $To:ty, $doc: expr) => {
+// integer -> non-zero integer
+impl_nonzero_int_try_from_int!(u8);
+impl_nonzero_int_try_from_int!(u16);
+impl_nonzero_int_try_from_int!(u32);
+impl_nonzero_int_try_from_int!(u64);
+impl_nonzero_int_try_from_int!(u128);
+impl_nonzero_int_try_from_int!(usize);
+impl_nonzero_int_try_from_int!(i8);
+impl_nonzero_int_try_from_int!(i16);
+impl_nonzero_int_try_from_int!(i32);
+impl_nonzero_int_try_from_int!(i64);
+impl_nonzero_int_try_from_int!(i128);
+impl_nonzero_int_try_from_int!(isize);
+
+macro_rules! impl_nonzero_int_try_from_nonzero_int {
+    ($source:ty => $($target:ty),+) => {$(
         #[stable(feature = "nzint_try_from_nzint_conv", since = "1.49.0")]
-        impl TryFrom<$From> for $To {
+        impl TryFrom<NonZero<$source>> for NonZero<$target> {
             type Error = TryFromIntError;
 
             // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
             // Rustdocs on functions do not.
-            #[doc = $doc]
+            #[doc = concat!("Attempts to convert <code>[NonZero]\\<[", stringify!($source), "]></code> ")]
+            #[doc = concat!("to <code>[NonZero]\\<[", stringify!($target), "]></code>.")]
             #[inline]
-            fn try_from(value: $From) -> Result<Self, Self::Error> {
-                TryFrom::try_from(value.get()).map(|v| {
-                    // SAFETY: $From is a NonZero type, so v is not zero.
-                    unsafe { Self::new_unchecked(v) }
-                })
+            fn try_from(value: NonZero<$source>) -> Result<Self, Self::Error> {
+                // SAFETY: Input is guaranteed to be non-zero.
+                Ok(unsafe { Self::new_unchecked(<$target>::try_from(value.get())?) })
             }
         }
-    };
-    ($To:ty: $($From: ty),*) => {$(
-        nzint_impl_try_from_nzint!(
-            $From => $To,
-            concat!(
-                "Attempts to convert `",
-                stringify!($From),
-                "` to `",
-                stringify!($To),
-                "`.",
-            )
-        );
     )*};
 }
 
-// Non-zero int -> non-zero unsigned int
-nzint_impl_try_from_nzint! { NonZeroU8: NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize }
-nzint_impl_try_from_nzint! { NonZeroU16: NonZeroI8, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize }
-nzint_impl_try_from_nzint! { NonZeroU32: NonZeroI8, NonZeroI16, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize }
-nzint_impl_try_from_nzint! { NonZeroU64: NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize }
-nzint_impl_try_from_nzint! { NonZeroU128: NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroUsize, NonZeroIsize }
-nzint_impl_try_from_nzint! { NonZeroUsize: NonZeroI8, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroIsize }
-
-// Non-zero int -> non-zero signed int
-nzint_impl_try_from_nzint! { NonZeroI8: NonZeroU8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize }
-nzint_impl_try_from_nzint! { NonZeroI16: NonZeroU16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize }
-nzint_impl_try_from_nzint! { NonZeroI32: NonZeroU32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize }
-nzint_impl_try_from_nzint! { NonZeroI64: NonZeroU64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize }
-nzint_impl_try_from_nzint! { NonZeroI128: NonZeroU128, NonZeroUsize, NonZeroIsize }
-nzint_impl_try_from_nzint! { NonZeroIsize: NonZeroU16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize }
+// unsigned non-zero integer -> unsigned non-zero integer
+impl_nonzero_int_try_from_nonzero_int!(u16 => u8);
+impl_nonzero_int_try_from_nonzero_int!(u32 => u8, u16, usize);
+impl_nonzero_int_try_from_nonzero_int!(u64 => u8, u16, u32, usize);
+impl_nonzero_int_try_from_nonzero_int!(u128 => u8, u16, u32, u64, usize);
+impl_nonzero_int_try_from_nonzero_int!(usize => u8, u16, u32, u64, u128);
+
+// signed non-zero integer -> signed non-zero integer
+impl_nonzero_int_try_from_nonzero_int!(i16 => i8);
+impl_nonzero_int_try_from_nonzero_int!(i32 => i8, i16, isize);
+impl_nonzero_int_try_from_nonzero_int!(i64 => i8, i16, i32, isize);
+impl_nonzero_int_try_from_nonzero_int!(i128 => i8, i16, i32, i64, isize);
+impl_nonzero_int_try_from_nonzero_int!(isize => i8, i16, i32, i64, i128);
+
+// unsigned non-zero integer -> signed non-zero integer
+impl_nonzero_int_try_from_nonzero_int!(u8 => i8);
+impl_nonzero_int_try_from_nonzero_int!(u16 => i8, i16, isize);
+impl_nonzero_int_try_from_nonzero_int!(u32 => i8, i16, i32, isize);
+impl_nonzero_int_try_from_nonzero_int!(u64 => i8, i16, i32, i64, isize);
+impl_nonzero_int_try_from_nonzero_int!(u128 => i8, i16, i32, i64, i128, isize);
+impl_nonzero_int_try_from_nonzero_int!(usize => i8, i16, i32, i64, i128, isize);
+
+// signed non-zero integer -> unsigned non-zero integer
+impl_nonzero_int_try_from_nonzero_int!(i8 => u8, u16, u32, u64, u128, usize);
+impl_nonzero_int_try_from_nonzero_int!(i16 => u8, u16, u32, u64, u128, usize);
+impl_nonzero_int_try_from_nonzero_int!(i32 => u8, u16, u32, u64, u128, usize);
+impl_nonzero_int_try_from_nonzero_int!(i64 => u8, u16, u32, u64, u128, usize);
+impl_nonzero_int_try_from_nonzero_int!(i128 => u8, u16, u32, u64, u128, usize);
+impl_nonzero_int_try_from_nonzero_int!(isize => u8, u16, u32, u64, u128, usize);

From 994d55158dcfe0b1b299591bb552af14c63c6e60 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 20 Feb 2024 02:13:28 +0000
Subject: [PATCH 02/12] Simply do not ICE

---
 .../rustc_trait_selection/src/solve/fulfill.rs    |  5 ++++-
 .../next-solver/coherence-fulfill-overflow.rs     | 15 +++++++++++++++
 .../next-solver/coherence-fulfill-overflow.stderr | 11 +++++++++++
 3 files changed, 30 insertions(+), 1 deletion(-)
 create mode 100644 tests/ui/traits/next-solver/coherence-fulfill-overflow.rs
 create mode 100644 tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr

diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 63555a305d855..97f715b6386c2 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -109,7 +109,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
         let mut errors = Vec::new();
         for i in 0.. {
             if !infcx.tcx.recursion_limit().value_within_limit(i) {
-                unimplemented!("overflowed on pending obligations: {:?}", self.obligations);
+                // Only return true errors that we have accumulated while processing;
+                // keep ambiguities around, *including overflows*, because they shouldn't
+                // be considered true errors.
+                return errors;
             }
 
             let mut has_changed = false;
diff --git a/tests/ui/traits/next-solver/coherence-fulfill-overflow.rs b/tests/ui/traits/next-solver/coherence-fulfill-overflow.rs
new file mode 100644
index 0000000000000..ff577da32c23a
--- /dev/null
+++ b/tests/ui/traits/next-solver/coherence-fulfill-overflow.rs
@@ -0,0 +1,15 @@
+//@ compile-flags: -Znext-solver=coherence
+
+#![recursion_limit = "10"]
+
+trait Trait {}
+
+struct W<T: ?Sized>(*const T);
+trait TwoW {}
+impl<T: ?Sized + TwoW> TwoW for W<W<T>> {}
+
+impl<T: ?Sized + TwoW> Trait for W<T> {}
+impl<T: ?Sized + TwoW> Trait for T {}
+//~^ ERROR conflicting implementations of trait `Trait` for type `W
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr b/tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr
new file mode 100644
index 0000000000000..406c0ccca9723
--- /dev/null
+++ b/tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr
@@ -0,0 +1,11 @@
+error[E0119]: conflicting implementations of trait `Trait` for type `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>`
+  --> $DIR/coherence-fulfill-overflow.rs:12:1
+   |
+LL | impl<T: ?Sized + TwoW> Trait for W<T> {}
+   | ------------------------------------- first implementation here
+LL | impl<T: ?Sized + TwoW> Trait for T {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0119`.

From 18fdf59bf5fc20d1c651f1d0e1dfae5cf421f685 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 20 Feb 2024 02:58:07 +0000
Subject: [PATCH 03/12] Don't use raw parameter types in find_builder_fn

---
 compiler/rustc_hir_typeck/src/method/suggest.rs | 8 ++++++--
 tests/ui/issues/issue-30123.stderr              | 5 +++++
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 4151298b42f0a..5aededa62bd79 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -34,8 +34,8 @@ use rustc_middle::ty::IsSuggestable;
 use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::def_id::DefIdSet;
 use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::Symbol;
 use rustc_span::{edit_distance, ExpnKind, FileName, MacroKind, Span};
+use rustc_span::{Symbol, DUMMY_SP};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
 use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
@@ -1597,7 +1597,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .filter(|item| matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter)
             .filter_map(|item| {
                 // Only assoc fns that return `Self`, `Option<Self>` or `Result<Self, _>`.
-                let ret_ty = self.tcx.fn_sig(item.def_id).skip_binder().output();
+                let ret_ty = self
+                    .tcx
+                    .fn_sig(item.def_id)
+                    .instantiate(self.tcx, self.fresh_args_for_item(DUMMY_SP, item.def_id))
+                    .output();
                 let ret_ty = self.tcx.instantiate_bound_regions_with_erased(ret_ty);
                 let ty::Adt(def, args) = ret_ty.kind() else {
                     return None;
diff --git a/tests/ui/issues/issue-30123.stderr b/tests/ui/issues/issue-30123.stderr
index cf71a01b58a5c..c086b45ac9bc0 100644
--- a/tests/ui/issues/issue-30123.stderr
+++ b/tests/ui/issues/issue-30123.stderr
@@ -4,6 +4,11 @@ error[E0599]: no function or associated item named `new_undirected` found for st
 LL |     let ug = Graph::<i32, i32>::new_undirected();
    |                                 ^^^^^^^^^^^^^^ function or associated item not found in `Graph<i32, i32>`
    |
+note: if you're trying to build a new `issue_30123_aux::Graph<i32, i32>`, consider using `issue_30123_aux::Graph::<N, E>::new` which returns `issue_30123_aux::Graph<_, _>`
+  --> $DIR/auxiliary/issue-30123-aux.rs:14:5
+   |
+LL |     pub fn new() -> Self {
+   |     ^^^^^^^^^^^^^^^^^^^^
    = note: the function or associated item was found for
            - `issue_30123_aux::Graph<N, E, Undirected>`
 

From 1c80aadb0566a344adc028b06f47f1a21ec21e0c Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 20 Feb 2024 03:01:35 +0000
Subject: [PATCH 04/12] test

---
 tests/ui/ufcs/bad-builder.rs     |  6 ++++++
 tests/ui/ufcs/bad-builder.stderr | 20 ++++++++++++++++++++
 2 files changed, 26 insertions(+)
 create mode 100644 tests/ui/ufcs/bad-builder.rs
 create mode 100644 tests/ui/ufcs/bad-builder.stderr

diff --git a/tests/ui/ufcs/bad-builder.rs b/tests/ui/ufcs/bad-builder.rs
new file mode 100644
index 0000000000000..350c96acca08b
--- /dev/null
+++ b/tests/ui/ufcs/bad-builder.rs
@@ -0,0 +1,6 @@
+fn hello<Q>() -> Vec<Q> {
+    Vec::<Q>::mew()
+    //~^ ERROR no function or associated item named `mew` found for struct `Vec<Q>` in the current scope
+}
+
+fn main() {}
diff --git a/tests/ui/ufcs/bad-builder.stderr b/tests/ui/ufcs/bad-builder.stderr
new file mode 100644
index 0000000000000..7fa47c82de2f7
--- /dev/null
+++ b/tests/ui/ufcs/bad-builder.stderr
@@ -0,0 +1,20 @@
+error[E0599]: no function or associated item named `mew` found for struct `Vec<Q>` in the current scope
+  --> $DIR/bad-builder.rs:2:15
+   |
+LL |     Vec::<Q>::mew()
+   |               ^^^
+   |               |
+   |               function or associated item not found in `Vec<Q>`
+   |               help: there is an associated function with a similar name: `new`
+   |
+note: if you're trying to build a new `Vec<Q>` consider using one of the following associated functions:
+      Vec::<T>::new
+      Vec::<T>::with_capacity
+      Vec::<T>::from_raw_parts
+      Vec::<T, A>::new_in
+      and 2 others
+  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0599`.

From 762febdaf33667995578ec8183a2379e28bb6b40 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 20 Feb 2024 15:43:29 +0000
Subject: [PATCH 05/12] Fix stray trait mismatch in resolve_associated_item for
 AsyncFn

---
 compiler/rustc_ty_utils/src/instance.rs       | 18 +++--------------
 .../async-fn/auxiliary/block-on.rs            | 20 +++++++++++++++++++
 tests/ui/async-await/async-fn/simple.rs       |  7 ++++++-
 3 files changed, 29 insertions(+), 16 deletions(-)
 create mode 100644 tests/ui/async-await/async-fn/auxiliary/block-on.rs

diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 5fc93d666ab16..3e3bccce47fe4 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -246,7 +246,7 @@ fn resolve_associated_item<'tcx>(
                         span: tcx.def_span(trait_item_id),
                     })
                 }
-            } else if tcx.fn_trait_kind_from_def_id(trait_ref.def_id).is_some() {
+            } else if let Some(target_kind) = tcx.fn_trait_kind_from_def_id(trait_ref.def_id) {
                 // FIXME: This doesn't check for malformed libcore that defines, e.g.,
                 // `trait Fn { fn call_once(&self) { .. } }`. This is mostly for extension
                 // methods.
@@ -265,13 +265,7 @@ fn resolve_associated_item<'tcx>(
                 }
                 match *rcvr_args.type_at(0).kind() {
                     ty::Closure(closure_def_id, args) => {
-                        let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
-                        Some(Instance::resolve_closure(
-                            tcx,
-                            closure_def_id,
-                            args,
-                            trait_closure_kind,
-                        ))
+                        Some(Instance::resolve_closure(tcx, closure_def_id, args, target_kind))
                     }
                     ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
                         def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)),
@@ -324,13 +318,7 @@ fn resolve_associated_item<'tcx>(
                         }
                     }
                     ty::Closure(closure_def_id, args) => {
-                        let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
-                        Some(Instance::resolve_closure(
-                            tcx,
-                            closure_def_id,
-                            args,
-                            trait_closure_kind,
-                        ))
+                        Some(Instance::resolve_closure(tcx, closure_def_id, args, target_kind))
                     }
                     ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
                         def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)),
diff --git a/tests/ui/async-await/async-fn/auxiliary/block-on.rs b/tests/ui/async-await/async-fn/auxiliary/block-on.rs
new file mode 100644
index 0000000000000..dcb710fc97c97
--- /dev/null
+++ b/tests/ui/async-await/async-fn/auxiliary/block-on.rs
@@ -0,0 +1,20 @@
+//@ edition: 2021
+
+#![feature(async_closure, noop_waker)]
+
+use std::future::Future;
+use std::pin::pin;
+use std::task::*;
+
+pub fn block_on<T>(fut: impl Future<Output = T>) -> T {
+    let mut fut = pin!(fut);
+    // Poll loop, just to test the future...
+    let ctx = &mut Context::from_waker(Waker::noop());
+
+    loop {
+        match unsafe { fut.as_mut().poll(ctx) } {
+            Poll::Pending => {}
+            Poll::Ready(t) => break t,
+        }
+    }
+}
diff --git a/tests/ui/async-await/async-fn/simple.rs b/tests/ui/async-await/async-fn/simple.rs
index e2a183a8c0ba0..21972ba5aefad 100644
--- a/tests/ui/async-await/async-fn/simple.rs
+++ b/tests/ui/async-await/async-fn/simple.rs
@@ -1,8 +1,11 @@
+//@ aux-build:block-on.rs
 //@ edition: 2021
 //@ build-pass
 
 #![feature(async_fn_traits)]
 
+extern crate block_on;
+
 use std::ops::AsyncFn;
 
 async fn foo() {}
@@ -12,5 +15,7 @@ async fn call_asyncly(f: impl AsyncFn(i32) -> i32) -> i32 {
 }
 
 fn main() {
-    let fut = call_asyncly(|x| async move { x + 1 });
+    block_on::block_on(async {
+        call_asyncly(|x| async move { x + 1 }).await;
+    });
 }

From 14a45516951edbbc9dd404529073dc9e9dd40351 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Oko=C5=84ski?= <jakub@okonski.org>
Date: Fri, 22 Dec 2023 00:15:44 +0100
Subject: [PATCH 06/12] Correct the simd_masked_{load,store} intrinsic docs

---
 library/core/src/intrinsics/simd.rs | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs
index 68c8a335b405c..f7818e611f401 100644
--- a/library/core/src/intrinsics/simd.rs
+++ b/library/core/src/intrinsics/simd.rs
@@ -243,12 +243,13 @@ extern "platform-intrinsic" {
     ///
     /// `T` must be a vector.
     ///
-    /// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`.
+    /// `U` must be a pointer to the element type of `T`
     ///
     /// `V` must be a vector of integers with the same length as `T` (but any element size).
     ///
     /// For each element, if the corresponding value in `mask` is `!0`, read the corresponding
-    /// pointer from `ptr`.
+    /// pointer offset from `ptr`.
+    /// The first element is loaded from `ptr`, the second from `ptr.wrapping_offset(1)` and so on.
     /// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from
     /// `val`.
     ///
@@ -264,12 +265,13 @@ extern "platform-intrinsic" {
     ///
     /// `T` must be a vector.
     ///
-    /// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`.
+    /// `U` must be a pointer to the element type of `T`
     ///
     /// `V` must be a vector of integers with the same length as `T` (but any element size).
     ///
     /// For each element, if the corresponding value in `mask` is `!0`, write the corresponding
-    /// value in `val` to the pointer.
+    /// value in `val` to the pointer offset from `ptr`.
+    /// The first element is written to `ptr`, the second to `ptr.wrapping_offset(1)` and so on.
     /// Otherwise if the corresponding value in `mask` is `0`, do nothing.
     ///
     /// # Safety

From 05ce209d20232f6d933421f8b8ac58d94557a2aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Tue, 20 Feb 2024 10:34:51 +0100
Subject: [PATCH 07/12] Rename some normalization-related items

---
 compiler/rustc_middle/src/arena.rs            |  2 +-
 compiler/rustc_middle/src/query/mod.rs        | 32 ++++++++++++------
 compiler/rustc_middle/src/traits/query.rs     |  6 ++--
 .../src/traits/normalize.rs                   |  2 --
 .../src/traits/query/normalize.rs             | 16 ++++-----
 .../src/normalize_projection_ty.rs            | 33 ++++++++++---------
 6 files changed, 51 insertions(+), 40 deletions(-)

diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index c648b2bfe9b90..a532635669dfd 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -47,7 +47,7 @@ macro_rules! arena_types {
                         rustc_middle::traits::query::DropckOutlivesResult<'tcx>
                     >
                 >,
-            [] normalize_projection_ty:
+            [] normalize_canonicalized_projection_ty:
                 rustc_middle::infer::canonical::Canonical<'tcx,
                     rustc_middle::infer::canonical::QueryResponse<'tcx,
                         rustc_middle::traits::query::NormalizationResult<'tcx>
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 5be45c33e1124..c931d2e5313aa 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -31,7 +31,7 @@ use crate::query::plumbing::{
 };
 use crate::thir;
 use crate::traits::query::{
-    CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
+    CanonicalAliasGoal, CanonicalPredicateGoal, CanonicalTyGoal,
     CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
     CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution,
 };
@@ -1931,9 +1931,13 @@ rustc_queries! {
         arena_cache
     }
 
-    /// Do not call this query directly: invoke `normalize` instead.
-    query normalize_projection_ty(
-        goal: CanonicalProjectionGoal<'tcx>
+    /// <div class="warning">
+    ///
+    /// Do not call this query directly: Invoke `normalize` instead.
+    ///
+    /// </div>
+    query normalize_canonicalized_projection_ty(
+        goal: CanonicalAliasGoal<'tcx>
     ) -> Result<
         &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
         NoSolution,
@@ -1941,9 +1945,13 @@ rustc_queries! {
         desc { "normalizing `{}`", goal.value.value }
     }
 
-    /// Do not call this query directly: invoke `normalize` instead.
-    query normalize_weak_ty(
-        goal: CanonicalProjectionGoal<'tcx>
+    /// <div class="warning">
+    ///
+    /// Do not call this query directly: Invoke `normalize` instead.
+    ///
+    /// </div>
+    query normalize_canonicalized_weak_ty(
+        goal: CanonicalAliasGoal<'tcx>
     ) -> Result<
         &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
         NoSolution,
@@ -1951,9 +1959,13 @@ rustc_queries! {
         desc { "normalizing `{}`", goal.value.value }
     }
 
-    /// Do not call this query directly: invoke `normalize` instead.
-    query normalize_inherent_projection_ty(
-        goal: CanonicalProjectionGoal<'tcx>
+    /// <div class="warning">
+    ///
+    /// Do not call this query directly: Invoke `normalize` instead.
+    ///
+    /// </div>
+    query normalize_canonicalized_inherent_projection_ty(
+        goal: CanonicalAliasGoal<'tcx>
     ) -> Result<
         &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
         NoSolution,
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index 61d96cad57d77..12e4f70ba5751 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -67,7 +67,7 @@ pub mod type_op {
     }
 }
 
-pub type CanonicalProjectionGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>;
+pub type CanonicalAliasGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>;
 
 pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>;
 
@@ -177,10 +177,10 @@ pub struct MethodAutoderefBadTy<'tcx> {
     pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
 }
 
-/// Result from the `normalize_projection_ty` query.
+/// Result of the `normalize_canonicalized_{{,inherent_}projection,weak}_ty` queries.
 #[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
 pub struct NormalizationResult<'tcx> {
-    /// Result of normalization.
+    /// Result of the normalization.
     pub normalized_ty: Ty<'tcx>,
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
index ac62f875fb910..f37d917b4a08e 100644
--- a/compiler/rustc_trait_selection/src/traits/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -355,8 +355,6 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
 
                 let data = data.fold_with(self);
 
-                // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement`
-                // here like `ty::Projection`?
                 project::normalize_inherent_projection(
                     self.selcx,
                     self.param_env,
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 26da065246d7e..70fd0b7e50b75 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -1,6 +1,6 @@
 //! Code for the 'normalization' query. This consists of a wrapper
 //! which folds deeply, invoking the underlying
-//! `normalize_projection_ty` query when it encounters projections.
+//! `normalize_canonicalized_projection_ty` query when it encounters projections.
 
 use crate::infer::at::At;
 use crate::infer::canonical::OriginalQueryValues;
@@ -271,9 +271,9 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                 debug!("QueryNormalizer: c_data = {:#?}", c_data);
                 debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
                 let result = match kind {
-                    ty::Projection => tcx.normalize_projection_ty(c_data),
-                    ty::Weak => tcx.normalize_weak_ty(c_data),
-                    ty::Inherent => tcx.normalize_inherent_projection_ty(c_data),
+                    ty::Projection => tcx.normalize_canonicalized_projection_ty(c_data),
+                    ty::Weak => tcx.normalize_canonicalized_weak_ty(c_data),
+                    ty::Inherent => tcx.normalize_canonicalized_inherent_projection_ty(c_data),
                     kind => unreachable!("did not expect {kind:?} due to match arm above"),
                 }?;
                 // We don't expect ambiguity.
@@ -308,10 +308,10 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                 } else {
                     result.normalized_ty
                 };
-                // `tcx.normalize_projection_ty` may normalize to a type that still has
-                // unevaluated consts, so keep normalizing here if that's the case.
-                // Similarly, `tcx.normalize_weak_ty` will only unwrap one layer of type
-                // and we need to continue folding it to reveal the TAIT behind it.
+                // `tcx.normalize_canonicalized_projection_ty` may normalize to a type that
+                // still has unevaluated consts, so keep normalizing here if that's the case.
+                // Similarly, `tcx.normalize_canonicalized_weak_ty` will only unwrap one layer
+                // of type and we need to continue folding it to reveal the TAIT behind it.
                 if res != ty
                     && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak)
                 {
diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs
index 94df28a145465..92a19fb91198c 100644
--- a/compiler/rustc_traits/src/normalize_projection_ty.rs
+++ b/compiler/rustc_traits/src/normalize_projection_ty.rs
@@ -5,7 +5,7 @@ use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
 use rustc_trait_selection::infer::InferCtxtBuilderExt;
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::query::{
-    normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution,
+    normalize::NormalizationResult, CanonicalAliasGoal, NoSolution,
 };
 use rustc_trait_selection::traits::{
     self, FulfillmentErrorCode, ObligationCause, SelectionContext,
@@ -13,18 +13,19 @@ use rustc_trait_selection::traits::{
 
 pub(crate) fn provide(p: &mut Providers) {
     *p = Providers {
-        normalize_projection_ty,
-        normalize_weak_ty,
-        normalize_inherent_projection_ty,
+        normalize_canonicalized_projection_ty,
+        normalize_canonicalized_weak_ty,
+        normalize_canonicalized_inherent_projection_ty,
         ..*p
     };
 }
 
-fn normalize_projection_ty<'tcx>(
+fn normalize_canonicalized_projection_ty<'tcx>(
     tcx: TyCtxt<'tcx>,
-    goal: CanonicalProjectionGoal<'tcx>,
+    goal: CanonicalAliasGoal<'tcx>,
 ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
-    debug!("normalize_provider(goal={:#?})", goal);
+    debug!("normalize_canonicalized_projection_ty(goal={:#?})", goal);
+
     tcx.infer_ctxt().enter_canonical_trait_query(
         &goal,
         |ocx, ParamEnvAnd { param_env, value: goal }| {
@@ -61,19 +62,19 @@ fn normalize_projection_ty<'tcx>(
                 return Err(NoSolution);
             }
 
-            // FIXME(associated_const_equality): All users of normalize_projection_ty expected
-            // a type, but there is the possibility it could've been a const now. Maybe change
-            // it to a Term later?
+            // FIXME(associated_const_equality): All users of normalize_canonicalized_projection_ty
+            // expected a type, but there is the possibility it could've been a const now.
+            // Maybe change it to a Term later?
             Ok(NormalizationResult { normalized_ty: answer.ty().unwrap() })
         },
     )
 }
 
-fn normalize_weak_ty<'tcx>(
+fn normalize_canonicalized_weak_ty<'tcx>(
     tcx: TyCtxt<'tcx>,
-    goal: CanonicalProjectionGoal<'tcx>,
+    goal: CanonicalAliasGoal<'tcx>,
 ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
-    debug!("normalize_provider(goal={:#?})", goal);
+    debug!("normalize_canonicalized_weak_ty(goal={:#?})", goal);
 
     tcx.infer_ctxt().enter_canonical_trait_query(
         &goal,
@@ -95,11 +96,11 @@ fn normalize_weak_ty<'tcx>(
     )
 }
 
-fn normalize_inherent_projection_ty<'tcx>(
+fn normalize_canonicalized_inherent_projection_ty<'tcx>(
     tcx: TyCtxt<'tcx>,
-    goal: CanonicalProjectionGoal<'tcx>,
+    goal: CanonicalAliasGoal<'tcx>,
 ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
-    debug!("normalize_provider(goal={:#?})", goal);
+    debug!("normalize_canonicalized_inherent_projection_ty(goal={:#?})", goal);
 
     tcx.infer_ctxt().enter_canonical_trait_query(
         &goal,

From 515d805a0e76b57853bf9e929a2e7b084c475824 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Tue, 20 Feb 2024 12:38:23 +0100
Subject: [PATCH 08/12] Introduce expand_weak_alias_tys

---
 compiler/rustc_middle/src/ty/flags.rs         |  5 +-
 compiler/rustc_middle/src/ty/util.rs          | 61 +++++++++++++++++++
 compiler/rustc_middle/src/ty/visit.rs         | 25 ++++----
 .../src/traits/normalize.rs                   | 20 +++---
 compiler/rustc_type_ir/src/flags.rs           | 31 +++++-----
 .../constrained-late-bound-regions.rs         | 15 +++++
 ...arams.rs => constrained-params-in-impl.rs} |  0
 .../unconstrained-late-bound-regions.rs       | 23 +++++++
 .../unconstrained-late-bound-regions.stderr   | 22 +++++++
 ...trained-params-in-impl-due-to-overflow.rs} |  0
 ...ned-params-in-impl-due-to-overflow.stderr} |  2 +-
 ...ams.rs => unconstrained-params-in-impl.rs} |  0
 ...rr => unconstrained-params-in-impl.stderr} |  2 +-
 13 files changed, 165 insertions(+), 41 deletions(-)
 create mode 100644 tests/ui/lazy-type-alias/constrained-late-bound-regions.rs
 rename tests/ui/lazy-type-alias/{constrained-params.rs => constrained-params-in-impl.rs} (100%)
 create mode 100644 tests/ui/lazy-type-alias/unconstrained-late-bound-regions.rs
 create mode 100644 tests/ui/lazy-type-alias/unconstrained-late-bound-regions.stderr
 rename tests/ui/lazy-type-alias/{unconstrained-param-due-to-overflow.rs => unconstrained-params-in-impl-due-to-overflow.rs} (100%)
 rename tests/ui/lazy-type-alias/{unconstrained-param-due-to-overflow.stderr => unconstrained-params-in-impl-due-to-overflow.stderr} (81%)
 rename tests/ui/lazy-type-alias/{unconstrained-params.rs => unconstrained-params-in-impl.rs} (100%)
 rename tests/ui/lazy-type-alias/{unconstrained-params.stderr => unconstrained-params-in-impl.stderr} (85%)

diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index 0f4b5fe228c96..18cf5445e5621 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -181,9 +181,10 @@ impl FlagComputation {
 
             &ty::Alias(kind, data) => {
                 self.add_flags(match kind {
-                    ty::Weak | ty::Projection => TypeFlags::HAS_TY_PROJECTION,
-                    ty::Inherent => TypeFlags::HAS_TY_INHERENT,
+                    ty::Projection => TypeFlags::HAS_TY_PROJECTION,
+                    ty::Weak => TypeFlags::HAS_TY_WEAK,
                     ty::Opaque => TypeFlags::HAS_TY_OPAQUE,
+                    ty::Inherent => TypeFlags::HAS_TY_INHERENT,
                 });
 
                 self.add_alias_ty(data);
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 3f539945841b6..ff9d1ed6ae995 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -11,6 +11,7 @@ use crate::ty::{GenericArgKind, GenericArgsRef};
 use rustc_apfloat::Float as _;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher};
+use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
@@ -867,6 +868,30 @@ impl<'tcx> TyCtxt<'tcx> {
 
         self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param))
     }
+
+    /// Expand any [weak alias types][weak] contained within the given `value`.
+    ///
+    /// This should be used over other normalization routines in situations where
+    /// it's important not to normalize other alias types and where the predicates
+    /// on the corresponding type alias shouldn't be taken into consideration.
+    ///
+    /// Whenever possible **prefer not to use this function**! Instead, use standard
+    /// normalization routines or if feasible don't normalize at all.
+    ///
+    /// This function comes in handy if you want to mimic the behavior of eager
+    /// type alias expansion in a localized manner.
+    ///
+    /// <div class="warning">
+    /// This delays a bug on overflow! Therefore you need to be certain that the
+    /// contained types get fully normalized at a later stage. Note that even on
+    /// overflow all well-behaved weak alias types get expanded correctly, so the
+    /// result is still useful.
+    /// </div>
+    ///
+    /// [weak]: ty::Weak
+    pub fn expand_weak_alias_tys<T: TypeFoldable<TyCtxt<'tcx>>>(self, value: T) -> T {
+        value.fold_with(&mut WeakAliasTypeExpander { tcx: self, depth: 0 })
+    }
 }
 
 struct OpaqueTypeExpander<'tcx> {
@@ -1002,6 +1027,42 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
     }
 }
 
+struct WeakAliasTypeExpander<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    depth: usize,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for WeakAliasTypeExpander<'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if !ty.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) {
+            return ty;
+        }
+        let ty::Alias(ty::Weak, alias) = ty.kind() else {
+            return ty.super_fold_with(self);
+        };
+        if !self.tcx.recursion_limit().value_within_limit(self.depth) {
+            let guar = self.tcx.dcx().delayed_bug("overflow expanding weak alias type");
+            return Ty::new_error(self.tcx, guar);
+        }
+
+        self.depth += 1;
+        ensure_sufficient_stack(|| {
+            self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).fold_with(self)
+        })
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        if !ct.ty().has_type_flags(ty::TypeFlags::HAS_TY_WEAK) {
+            return ct;
+        }
+        ct.super_fold_with(self)
+    }
+}
+
 impl<'tcx> Ty<'tcx> {
     /// Returns the `Size` for primitive types (bool, uint, int, char, float).
     pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size {
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index 59292a281edec..1de2ceecae798 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -131,12 +131,13 @@ impl<'tcx> TyCtxt<'tcx> {
     fn collect_late_bound_regions<T>(
         self,
         value: &Binder<'tcx, T>,
-        just_constraint: bool,
+        just_constrained: bool,
     ) -> FxHashSet<ty::BoundRegionKind>
     where
         T: TypeVisitable<TyCtxt<'tcx>>,
     {
-        let mut collector = LateBoundRegionsCollector::new(just_constraint);
+        let mut collector = LateBoundRegionsCollector::new(self, just_constrained);
+        let value = if just_constrained { self.expand_weak_alias_tys(value) } else { value };
         let result = value.as_ref().skip_binder().visit_with(&mut collector);
         assert!(result.is_continue()); // should never have stopped early
         collector.regions
@@ -258,11 +259,7 @@ struct LateBoundRegionsCollector {
 
 impl LateBoundRegionsCollector {
     fn new(just_constrained: bool) -> Self {
-        LateBoundRegionsCollector {
-            current_index: ty::INNERMOST,
-            regions: Default::default(),
-            just_constrained,
-        }
+        Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained }
     }
 }
 
@@ -278,12 +275,16 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
     }
 
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-        // if we are only looking for "constrained" region, we have to
-        // ignore the inputs to a projection, as they may not appear
-        // in the normalized form
         if self.just_constrained {
-            if let ty::Alias(..) = t.kind() {
-                return ControlFlow::Continue(());
+            match t.kind() {
+                // If we are only looking for "constrained" regions, we have to ignore the
+                // inputs to a projection as they may not appear in the normalized form.
+                ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) => {
+                    return ControlFlow::Continue(());
+                }
+                // All weak alias types should've been expanded beforehand.
+                ty::Alias(ty::Weak, _) => bug!("unexpected weak alias type"),
+                _ => {}
             }
         }
 
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
index f37d917b4a08e..429e5a5d7a413 100644
--- a/compiler/rustc_trait_selection/src/traits/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -101,19 +101,17 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
     value: &T,
     reveal: Reveal,
 ) -> bool {
+    let mut flags = ty::TypeFlags::HAS_TY_PROJECTION
+        | ty::TypeFlags::HAS_TY_WEAK
+        | ty::TypeFlags::HAS_TY_INHERENT
+        | ty::TypeFlags::HAS_CT_PROJECTION;
+
     match reveal {
-        Reveal::UserFacing => value.has_type_flags(
-            ty::TypeFlags::HAS_TY_PROJECTION
-                | ty::TypeFlags::HAS_TY_INHERENT
-                | ty::TypeFlags::HAS_CT_PROJECTION,
-        ),
-        Reveal::All => value.has_type_flags(
-            ty::TypeFlags::HAS_TY_PROJECTION
-                | ty::TypeFlags::HAS_TY_INHERENT
-                | ty::TypeFlags::HAS_TY_OPAQUE
-                | ty::TypeFlags::HAS_CT_PROJECTION,
-        ),
+        Reveal::UserFacing => {}
+        Reveal::All => flags |= ty::TypeFlags::HAS_TY_OPAQUE,
     }
+
+    value.has_type_flags(flags)
 }
 
 struct AssocTypeNormalizer<'a, 'b, 'tcx> {
diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs
index 1da26cfc24275..b38ef2ad84d48 100644
--- a/compiler/rustc_type_ir/src/flags.rs
+++ b/compiler/rustc_type_ir/src/flags.rs
@@ -69,32 +69,35 @@ bitflags! {
 
         /// Does this have `Projection`?
         const HAS_TY_PROJECTION           = 1 << 10;
-        /// Does this have `Inherent`?
-        const HAS_TY_INHERENT             = 1 << 11;
+        /// Does this have `Weak`?
+        const HAS_TY_WEAK                 = 1 << 11;
         /// Does this have `Opaque`?
         const HAS_TY_OPAQUE               = 1 << 12;
+        /// Does this have `Inherent`?
+        const HAS_TY_INHERENT             = 1 << 13;
         /// Does this have `ConstKind::Unevaluated`?
-        const HAS_CT_PROJECTION           = 1 << 13;
+        const HAS_CT_PROJECTION           = 1 << 14;
 
         /// Could this type be normalized further?
         const HAS_PROJECTION              = TypeFlags::HAS_TY_PROJECTION.bits()
+                                          | TypeFlags::HAS_TY_WEAK.bits()
                                           | TypeFlags::HAS_TY_OPAQUE.bits()
                                           | TypeFlags::HAS_TY_INHERENT.bits()
                                           | TypeFlags::HAS_CT_PROJECTION.bits();
 
         /// Is an error type/const reachable?
-        const HAS_ERROR                   = 1 << 14;
+        const HAS_ERROR                   = 1 << 15;
 
         /// Does this have any region that "appears free" in the type?
         /// Basically anything but `ReBound` and `ReErased`.
-        const HAS_FREE_REGIONS            = 1 << 15;
+        const HAS_FREE_REGIONS            = 1 << 16;
 
         /// Does this have any `ReBound` regions?
-        const HAS_RE_BOUND                = 1 << 16;
+        const HAS_RE_BOUND                = 1 << 17;
         /// Does this have any `Bound` types?
-        const HAS_TY_BOUND                = 1 << 17;
+        const HAS_TY_BOUND                = 1 << 18;
         /// Does this have any `ConstKind::Bound` consts?
-        const HAS_CT_BOUND                = 1 << 18;
+        const HAS_CT_BOUND                = 1 << 19;
         /// Does this have any bound variables?
         /// Used to check if a global bound is safe to evaluate.
         const HAS_BOUND_VARS              = TypeFlags::HAS_RE_BOUND.bits()
@@ -102,22 +105,22 @@ bitflags! {
                                           | TypeFlags::HAS_CT_BOUND.bits();
 
         /// Does this have any `ReErased` regions?
-        const HAS_RE_ERASED               = 1 << 19;
+        const HAS_RE_ERASED               = 1 << 20;
 
         /// Does this value have parameters/placeholders/inference variables which could be
         /// replaced later, in a way that would change the results of `impl` specialization?
-        const STILL_FURTHER_SPECIALIZABLE = 1 << 20;
+        const STILL_FURTHER_SPECIALIZABLE = 1 << 21;
 
         /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`?
-        const HAS_TY_FRESH                = 1 << 21;
+        const HAS_TY_FRESH                = 1 << 22;
 
         /// Does this value have `InferConst::Fresh`?
-        const HAS_CT_FRESH                = 1 << 22;
+        const HAS_CT_FRESH                = 1 << 23;
 
         /// Does this have `Coroutine` or `CoroutineWitness`?
-        const HAS_TY_COROUTINE            = 1 << 23;
+        const HAS_TY_COROUTINE            = 1 << 24;
 
         /// Does this have any binders with bound vars (e.g. that need to be anonymized)?
-        const HAS_BINDER_VARS             = 1 << 24;
+        const HAS_BINDER_VARS             = 1 << 25;
     }
 }
diff --git a/tests/ui/lazy-type-alias/constrained-late-bound-regions.rs b/tests/ui/lazy-type-alias/constrained-late-bound-regions.rs
new file mode 100644
index 0000000000000..e759e72d745a1
--- /dev/null
+++ b/tests/ui/lazy-type-alias/constrained-late-bound-regions.rs
@@ -0,0 +1,15 @@
+//@ check-pass
+// Weak alias types constrain late-bound regions if their normalized form constrains them.
+
+#![feature(lazy_type_alias)]
+#![allow(incomplete_features)]
+
+type Ref<'a> = &'a ();
+
+type FnPtr = for<'a> fn(Ref<'a>) -> &'a (); // OK
+type DynCl = dyn for<'a> Fn(Ref<'a>) -> &'a (); // OK
+
+fn map0(_: Ref) -> Ref { &() } // OK
+fn map1(_: Ref<'_>) -> Ref<'_> { &() } // OK
+
+fn main() {}
diff --git a/tests/ui/lazy-type-alias/constrained-params.rs b/tests/ui/lazy-type-alias/constrained-params-in-impl.rs
similarity index 100%
rename from tests/ui/lazy-type-alias/constrained-params.rs
rename to tests/ui/lazy-type-alias/constrained-params-in-impl.rs
diff --git a/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.rs b/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.rs
new file mode 100644
index 0000000000000..844570e22d275
--- /dev/null
+++ b/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.rs
@@ -0,0 +1,23 @@
+// Weak alias types only constrain late-bound regions if their normalized form constrains them.
+
+#![feature(lazy_type_alias)]
+#![allow(incomplete_features)]
+
+type NotInjective<'a> = <() as Discard>::Output<'a>;
+
+type FnPtr0 = for<'a> fn(NotInjective<'a>) -> &'a ();
+//~^ ERROR references lifetime `'a`, which is not constrained by the fn input types
+type FnPtr1 = for<'a> fn(NotInjectiveEither<'a, ()>) -> NotInjectiveEither<'a, ()>;
+//~^ ERROR references lifetime `'a`, which is not constrained by the fn input types
+type DynCl = dyn for<'a> Fn(NotInjective<'a>) -> &'a ();
+//~^ ERROR references lifetime `'a`, which does not appear in the trait input types
+
+trait Discard { type Output<'a>; }
+impl Discard for () { type Output<'_a> = (); }
+
+type NotInjectiveEither<'a, Linchpin> = Linchpin
+where
+    Linchpin: Fn() -> &'a ();
+
+
+fn main() {}
diff --git a/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.stderr b/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.stderr
new file mode 100644
index 0000000000000..241c7761c60f5
--- /dev/null
+++ b/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.stderr
@@ -0,0 +1,22 @@
+error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types
+  --> $DIR/unconstrained-late-bound-regions.rs:8:47
+   |
+LL | type FnPtr0 = for<'a> fn(NotInjective<'a>) -> &'a ();
+   |                                               ^^^^^^
+
+error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types
+  --> $DIR/unconstrained-late-bound-regions.rs:10:57
+   |
+LL | type FnPtr1 = for<'a> fn(NotInjectiveEither<'a, ()>) -> NotInjectiveEither<'a, ()>;
+   |                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types
+  --> $DIR/unconstrained-late-bound-regions.rs:12:50
+   |
+LL | type DynCl = dyn for<'a> Fn(NotInjective<'a>) -> &'a ();
+   |                                                  ^^^^^^
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0581, E0582.
+For more information about an error, try `rustc --explain E0581`.
diff --git a/tests/ui/lazy-type-alias/unconstrained-param-due-to-overflow.rs b/tests/ui/lazy-type-alias/unconstrained-params-in-impl-due-to-overflow.rs
similarity index 100%
rename from tests/ui/lazy-type-alias/unconstrained-param-due-to-overflow.rs
rename to tests/ui/lazy-type-alias/unconstrained-params-in-impl-due-to-overflow.rs
diff --git a/tests/ui/lazy-type-alias/unconstrained-param-due-to-overflow.stderr b/tests/ui/lazy-type-alias/unconstrained-params-in-impl-due-to-overflow.stderr
similarity index 81%
rename from tests/ui/lazy-type-alias/unconstrained-param-due-to-overflow.stderr
rename to tests/ui/lazy-type-alias/unconstrained-params-in-impl-due-to-overflow.stderr
index 9af6f5dda0b34..b65c84226ce93 100644
--- a/tests/ui/lazy-type-alias/unconstrained-param-due-to-overflow.stderr
+++ b/tests/ui/lazy-type-alias/unconstrained-params-in-impl-due-to-overflow.stderr
@@ -1,5 +1,5 @@
 error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
-  --> $DIR/unconstrained-param-due-to-overflow.rs:4:6
+  --> $DIR/unconstrained-params-in-impl-due-to-overflow.rs:4:6
    |
 LL | impl<T> Loop<T> {}
    |      ^ unconstrained type parameter
diff --git a/tests/ui/lazy-type-alias/unconstrained-params.rs b/tests/ui/lazy-type-alias/unconstrained-params-in-impl.rs
similarity index 100%
rename from tests/ui/lazy-type-alias/unconstrained-params.rs
rename to tests/ui/lazy-type-alias/unconstrained-params-in-impl.rs
diff --git a/tests/ui/lazy-type-alias/unconstrained-params.stderr b/tests/ui/lazy-type-alias/unconstrained-params-in-impl.stderr
similarity index 85%
rename from tests/ui/lazy-type-alias/unconstrained-params.stderr
rename to tests/ui/lazy-type-alias/unconstrained-params-in-impl.stderr
index 3c52a06c319db..2419c78cba8d9 100644
--- a/tests/ui/lazy-type-alias/unconstrained-params.stderr
+++ b/tests/ui/lazy-type-alias/unconstrained-params-in-impl.stderr
@@ -1,5 +1,5 @@
 error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
-  --> $DIR/unconstrained-params.rs:4:6
+  --> $DIR/unconstrained-params-in-impl.rs:4:6
    |
 LL | impl<T> NotInjective<T> {}
    |      ^ unconstrained type parameter

From da01cced15b1a59e5eb40bdf5bb0be9a143d4e5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Tue, 20 Feb 2024 13:50:39 +0100
Subject: [PATCH 09/12] Expand weak alias types before collecting constrained
 and referenced late bound regions

---
 .../rustc_hir_analysis/src/astconv/bounds.rs   |  4 ++--
 compiler/rustc_hir_analysis/src/astconv/mod.rs |  4 ++--
 compiler/rustc_hir_analysis/src/collect.rs     |  2 +-
 .../error_reporting/nice_region_error/util.rs  |  6 +++---
 compiler/rustc_middle/src/ty/visit.rs          | 18 ++++++++++--------
 src/librustdoc/clean/auto_trait.rs             | 17 ++++++++---------
 6 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
index 6940b4a504584..6d8a5bc0e9008 100644
--- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
@@ -454,9 +454,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
                     //     for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
                     //     for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
                     let late_bound_in_projection_ty =
-                        tcx.collect_constrained_late_bound_regions(&projection_ty);
+                        tcx.collect_constrained_late_bound_regions(projection_ty);
                     let late_bound_in_term =
-                        tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(term));
+                        tcx.collect_referenced_late_bound_regions(trait_ref.rebind(term));
                     debug!(?late_bound_in_projection_ty);
                     debug!(?late_bound_in_term);
 
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 296b63a8292d6..997392b6c4a7f 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -2678,9 +2678,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         //     for<'a> fn(&'a String) -> &'a str <-- 'a is ok
         let inputs = bare_fn_ty.inputs();
         let late_bound_in_args =
-            tcx.collect_constrained_late_bound_regions(&inputs.map_bound(|i| i.to_owned()));
+            tcx.collect_constrained_late_bound_regions(inputs.map_bound(|i| i.to_owned()));
         let output = bare_fn_ty.output();
-        let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output);
+        let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(output);
 
         self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| {
             struct_span_code_err!(
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index cffb88a1365b2..6a42fdb1079f4 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -520,7 +520,7 @@ fn get_new_lifetime_name<'tcx>(
     generics: &hir::Generics<'tcx>,
 ) -> String {
     let existing_lifetimes = tcx
-        .collect_referenced_late_bound_regions(&poly_trait_ref)
+        .collect_referenced_late_bound_regions(poly_trait_ref)
         .into_iter()
         .filter_map(|lt| {
             if let ty::BoundRegionKind::BrNamed(_, name) = lt {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
index bfff00b948e96..f1f8314661f3e 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
@@ -5,7 +5,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError;
 use crate::infer::TyCtxt;
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
-use rustc_middle::ty::{self, Binder, Region, Ty, TypeVisitable};
+use rustc_middle::ty::{self, Binder, Region, Ty, TypeFoldable};
 use rustc_span::Span;
 
 /// Information about the anonymous region we are searching for.
@@ -142,10 +142,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
 
     fn includes_region(
         &self,
-        ty: Binder<'tcx, impl TypeVisitable<TyCtxt<'tcx>>>,
+        ty: Binder<'tcx, impl TypeFoldable<TyCtxt<'tcx>>>,
         region: ty::BoundRegionKind,
     ) -> bool {
-        let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(&ty);
+        let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty);
         // We are only checking is any region meets the condition so order doesn't matter
         #[allow(rustc::potential_query_instability)]
         late_bound_regions.iter().any(|r| *r == region)
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index 1de2ceecae798..59d09c3dc7821 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -2,6 +2,7 @@ use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags};
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sso::SsoHashSet;
+use rustc_type_ir::fold::TypeFoldable;
 use std::ops::ControlFlow;
 
 pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
@@ -109,10 +110,10 @@ impl<'tcx> TyCtxt<'tcx> {
     /// variables will also be equated.
     pub fn collect_constrained_late_bound_regions<T>(
         self,
-        value: &Binder<'tcx, T>,
+        value: Binder<'tcx, T>,
     ) -> FxHashSet<ty::BoundRegionKind>
     where
-        T: TypeVisitable<TyCtxt<'tcx>>,
+        T: TypeFoldable<TyCtxt<'tcx>>,
     {
         self.collect_late_bound_regions(value, true)
     }
@@ -120,25 +121,26 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Returns a set of all late-bound regions that appear in `value` anywhere.
     pub fn collect_referenced_late_bound_regions<T>(
         self,
-        value: &Binder<'tcx, T>,
+        value: Binder<'tcx, T>,
     ) -> FxHashSet<ty::BoundRegionKind>
     where
-        T: TypeVisitable<TyCtxt<'tcx>>,
+        T: TypeFoldable<TyCtxt<'tcx>>,
     {
         self.collect_late_bound_regions(value, false)
     }
 
     fn collect_late_bound_regions<T>(
         self,
-        value: &Binder<'tcx, T>,
+        value: Binder<'tcx, T>,
         just_constrained: bool,
     ) -> FxHashSet<ty::BoundRegionKind>
     where
-        T: TypeVisitable<TyCtxt<'tcx>>,
+        T: TypeFoldable<TyCtxt<'tcx>>,
     {
-        let mut collector = LateBoundRegionsCollector::new(self, just_constrained);
+        let mut collector = LateBoundRegionsCollector::new(just_constrained);
+        let value = value.skip_binder();
         let value = if just_constrained { self.expand_weak_alias_tys(value) } else { value };
-        let result = value.as_ref().skip_binder().visit_with(&mut collector);
+        let result = value.visit_with(&mut collector);
         assert!(result.is_continue()); // should never have stopped early
         collector.regions
     }
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index 8cc4201c3fc25..fbc2c3c5af459 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -318,15 +318,14 @@ where
     fn extract_for_generics(&self, pred: ty::Clause<'tcx>) -> FxHashSet<GenericParamDef> {
         let bound_predicate = pred.kind();
         let tcx = self.cx.tcx;
-        let regions = match bound_predicate.skip_binder() {
-            ty::ClauseKind::Trait(poly_trait_pred) => {
-                tcx.collect_referenced_late_bound_regions(&bound_predicate.rebind(poly_trait_pred))
-            }
-            ty::ClauseKind::Projection(poly_proj_pred) => {
-                tcx.collect_referenced_late_bound_regions(&bound_predicate.rebind(poly_proj_pred))
-            }
-            _ => return FxHashSet::default(),
-        };
+        let regions =
+            match bound_predicate.skip_binder() {
+                ty::ClauseKind::Trait(poly_trait_pred) => tcx
+                    .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_trait_pred)),
+                ty::ClauseKind::Projection(poly_proj_pred) => tcx
+                    .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_proj_pred)),
+                _ => return FxHashSet::default(),
+            };
 
         regions
             .into_iter()

From 1b3df6f068cb676486c511890148dbb7981b62b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Tue, 20 Feb 2024 14:29:50 +0100
Subject: [PATCH 10/12] Use expand_weak_alias_tys when collecting constrained
 generics params in impls

---
 .../src/constrained_generic_params.rs         | 53 +++++++------------
 .../rustc_hir_analysis/src/impl_wf_check.rs   |  2 +-
 .../src/impl_wf_check/min_specialization.rs   | 10 ++--
 3 files changed, 25 insertions(+), 40 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
index 4ce43bb48871d..b8de2e46934dd 100644
--- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
+++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
@@ -1,8 +1,8 @@
 use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
+use rustc_type_ir::fold::TypeFoldable;
 use std::ops::ControlFlow;
 
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
@@ -33,62 +33,47 @@ pub fn parameters_for_impl<'tcx>(
     impl_trait_ref: Option<ty::TraitRef<'tcx>>,
 ) -> FxHashSet<Parameter> {
     let vec = match impl_trait_ref {
-        Some(tr) => parameters_for(tcx, &tr, false),
-        None => parameters_for(tcx, &impl_self_ty, false),
+        Some(tr) => parameters_for(tcx, tr, false),
+        None => parameters_for(tcx, impl_self_ty, false),
     };
     vec.into_iter().collect()
 }
 
 /// If `include_nonconstraining` is false, returns the list of parameters that are
-/// constrained by `t` - i.e., the value of each parameter in the list is
-/// uniquely determined by `t` (see RFC 447). If it is true, return the list
-/// of parameters whose values are needed in order to constrain `ty` - these
+/// constrained by `value` - i.e., the value of each parameter in the list is
+/// uniquely determined by `value` (see RFC 447). If it is true, return the list
+/// of parameters whose values are needed in order to constrain `value` - these
 /// differ, with the latter being a superset, in the presence of projections.
 pub fn parameters_for<'tcx>(
     tcx: TyCtxt<'tcx>,
-    t: &impl TypeVisitable<TyCtxt<'tcx>>,
+    value: impl TypeFoldable<TyCtxt<'tcx>>,
     include_nonconstraining: bool,
 ) -> Vec<Parameter> {
-    let mut collector =
-        ParameterCollector { tcx, parameters: vec![], include_nonconstraining, depth: 0 };
-    t.visit_with(&mut collector);
+    let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining };
+    let value = if !include_nonconstraining { tcx.expand_weak_alias_tys(value) } else { value };
+    value.visit_with(&mut collector);
     collector.parameters
 }
 
-struct ParameterCollector<'tcx> {
-    tcx: TyCtxt<'tcx>,
+struct ParameterCollector {
     parameters: Vec<Parameter>,
     include_nonconstraining: bool,
-    depth: usize,
 }
 
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector<'tcx> {
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector {
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         match *t.kind() {
+            // Projections are not injective in general.
             ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _)
                 if !self.include_nonconstraining =>
             {
-                // Projections are not injective in general.
                 return ControlFlow::Continue(());
             }
-            ty::Alias(ty::Weak, alias) if !self.include_nonconstraining => {
-                if !self.tcx.recursion_limit().value_within_limit(self.depth) {
-                    // Other constituent types may still constrain some generic params, consider
-                    // `<T> (Overflow, T)` for example. Therefore we want to continue instead of
-                    // breaking. Only affects diagnostics.
-                    return ControlFlow::Continue(());
-                }
-                self.depth += 1;
-                return ensure_sufficient_stack(|| {
-                    self.tcx
-                        .type_of(alias.def_id)
-                        .instantiate(self.tcx, alias.args)
-                        .visit_with(self)
-                });
-            }
-            ty::Param(data) => {
-                self.parameters.push(Parameter::from(data));
+            // All weak alias types should've been expanded beforehand.
+            ty::Alias(ty::Weak, _) if !self.include_nonconstraining => {
+                bug!("unexpected weak alias type")
             }
+            ty::Param(param) => self.parameters.push(Parameter::from(param)),
             _ => {}
         }
 
@@ -224,12 +209,12 @@ pub fn setup_constraining_predicates<'tcx>(
                 //     `<<T as Bar>::Baz as Iterator>::Output = <U as Iterator>::Output`
                 // Then the projection only applies if `T` is known, but it still
                 // does not determine `U`.
-                let inputs = parameters_for(tcx, &projection.projection_ty, true);
+                let inputs = parameters_for(tcx, projection.projection_ty, true);
                 let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(p));
                 if !relies_only_on_inputs {
                     continue;
                 }
-                input_parameters.extend(parameters_for(tcx, &projection.term, false));
+                input_parameters.extend(parameters_for(tcx, projection.term, false));
             } else {
                 continue;
             }
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
index b4cec1d9882b6..9d7866fe3e0cb 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
@@ -111,7 +111,7 @@ fn enforce_impl_params_are_constrained(
             match item.kind {
                 ty::AssocKind::Type => {
                     if item.defaultness(tcx).has_value() {
-                        cgp::parameters_for(tcx, &tcx.type_of(def_id).instantiate_identity(), true)
+                        cgp::parameters_for(tcx, tcx.type_of(def_id).instantiate_identity(), true)
                     } else {
                         vec![]
                     }
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index bd4fce81377d4..be1be1a135407 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -133,7 +133,7 @@ fn check_always_applicable(
 
     res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span));
     res = res.and(check_static_lifetimes(tcx, &parent_args, span));
-    res = res.and(check_duplicate_params(tcx, impl1_args, &parent_args, span));
+    res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span));
     res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span));
 
     res
@@ -266,15 +266,15 @@ fn unconstrained_parent_impl_args<'tcx>(
                 continue;
             }
 
-            unconstrained_parameters.extend(cgp::parameters_for(tcx, &projection_ty, true));
+            unconstrained_parameters.extend(cgp::parameters_for(tcx, projection_ty, true));
 
-            for param in cgp::parameters_for(tcx, &projected_ty, false) {
+            for param in cgp::parameters_for(tcx, projected_ty, false) {
                 if !unconstrained_parameters.contains(&param) {
                     constrained_params.insert(param.0);
                 }
             }
 
-            unconstrained_parameters.extend(cgp::parameters_for(tcx, &projected_ty, true));
+            unconstrained_parameters.extend(cgp::parameters_for(tcx, projected_ty, true));
         }
     }
 
@@ -309,7 +309,7 @@ fn unconstrained_parent_impl_args<'tcx>(
 fn check_duplicate_params<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl1_args: GenericArgsRef<'tcx>,
-    parent_args: &Vec<GenericArg<'tcx>>,
+    parent_args: Vec<GenericArg<'tcx>>,
     span: Span,
 ) -> Result<(), ErrorGuaranteed> {
     let mut base_params = cgp::parameters_for(tcx, parent_args, true);

From f515f99e9149b1f9c8d247b1d0fd543b1cd1fd37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Tue, 20 Feb 2024 15:08:15 +0100
Subject: [PATCH 11/12] Move the peeling function for weak alias types

---
 .../src/coherence/inherent_impls.rs           | 29 +---------------
 compiler/rustc_middle/src/ty/util.rs          | 33 +++++++++++++++++++
 2 files changed, 34 insertions(+), 28 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
index 4823472cf9617..32f312012548a 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
@@ -144,7 +144,7 @@ impl<'tcx> InherentCollect<'tcx> {
         let id = id.owner_id.def_id;
         let item_span = self.tcx.def_span(id);
         let self_ty = self.tcx.type_of(id).instantiate_identity();
-        let self_ty = peel_off_weak_aliases(self.tcx, self_ty);
+        let self_ty = self.tcx.peel_off_weak_alias_tys(self_ty);
         match *self_ty.kind() {
             ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()),
             ty::Foreign(did) => self.check_def_id(id, self_ty, did),
@@ -186,30 +186,3 @@ impl<'tcx> InherentCollect<'tcx> {
         }
     }
 }
-
-/// Peel off all weak alias types in this type until there are none left.
-///
-/// <div class="warning">
-///
-/// This assumes that `ty` gets normalized later and that any overflows occurring
-/// during said normalization get reported.
-///
-/// </div>
-fn peel_off_weak_aliases<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Ty<'tcx> {
-    let ty::Alias(ty::Weak, _) = ty.kind() else { return ty };
-
-    let limit = tcx.recursion_limit();
-    let mut depth = 0;
-
-    while let ty::Alias(ty::Weak, alias) = ty.kind() {
-        if !limit.value_within_limit(depth) {
-            let guar = tcx.dcx().delayed_bug("overflow expanding weak alias type");
-            return Ty::new_error(tcx, guar);
-        }
-
-        ty = tcx.type_of(alias.def_id).instantiate(tcx, alias.args);
-        depth += 1;
-    }
-
-    ty
-}
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index ff9d1ed6ae995..72a1905c147ae 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -892,6 +892,39 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn expand_weak_alias_tys<T: TypeFoldable<TyCtxt<'tcx>>>(self, value: T) -> T {
         value.fold_with(&mut WeakAliasTypeExpander { tcx: self, depth: 0 })
     }
+
+    /// Peel off all [weak alias types] in this type until there are none left.
+    ///
+    /// This only expands weak alias types in “head” / outermost positions. It can
+    /// be used over [expand_weak_alias_tys] as an optimization in situations where
+    /// one only really cares about the *kind* of the final aliased type but not
+    /// the types the other constituent types alias.
+    ///
+    /// <div class="warning">
+    /// This delays a bug on overflow! Therefore you need to be certain that the
+    /// type gets fully normalized at a later stage.
+    /// </div>
+    ///
+    /// [weak]: ty::Weak
+    /// [expand_weak_alias_tys]: Self::expand_weak_alias_tys
+    pub fn peel_off_weak_alias_tys(self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
+        let ty::Alias(ty::Weak, _) = ty.kind() else { return ty };
+
+        let limit = self.recursion_limit();
+        let mut depth = 0;
+
+        while let ty::Alias(ty::Weak, alias) = ty.kind() {
+            if !limit.value_within_limit(depth) {
+                let guar = self.dcx().delayed_bug("overflow expanding weak alias type");
+                return Ty::new_error(self, guar);
+            }
+
+            ty = self.type_of(alias.def_id).instantiate(self, alias.args);
+            depth += 1;
+        }
+
+        ty
+    }
 }
 
 struct OpaqueTypeExpander<'tcx> {

From 9ac73cbdc60bb7c32b72e9e963ed1a90da1c022f Mon Sep 17 00:00:00 2001
From: Malobre <mael.obrejan@protonmail.com>
Date: Tue, 20 Feb 2024 18:05:55 +0100
Subject: [PATCH 12/12] docs: add missing "the" to `str::strip_prefix` doc

---
 library/core/src/str/mod.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index e11d13f8bed16..f965a50058b1b 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -2192,8 +2192,8 @@ impl str {
 
     /// Returns a string slice with the prefix removed.
     ///
-    /// If the string starts with the pattern `prefix`, returns substring after the prefix, wrapped
-    /// in `Some`.  Unlike `trim_start_matches`, this method removes the prefix exactly once.
+    /// If the string starts with the pattern `prefix`, returns the substring after the prefix,
+    /// wrapped in `Some`. Unlike `trim_start_matches`, this method removes the prefix exactly once.
     ///
     /// If the string does not start with `prefix`, returns `None`.
     ///