From c5c93626a08f191549fa08c52f1617ac5d2b62b5 Mon Sep 17 00:00:00 2001
From: Mara Bos <m-ou.se@m-ou.se>
Date: Tue, 15 Apr 2025 17:29:04 +0200
Subject: [PATCH 1/8] Stabilize proc_macro::Span::{start, end, line, column}.

---
 library/proc_macro/src/lib.rs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index c46dcebedcab8..36a1c57b02012 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -513,13 +513,13 @@ impl Span {
     }
 
     /// Creates an empty span pointing to directly before this span.
-    #[unstable(feature = "proc_macro_span", issue = "54725")]
+    #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")]
     pub fn start(&self) -> Span {
         Span(self.0.start())
     }
 
     /// Creates an empty span pointing to directly after this span.
-    #[unstable(feature = "proc_macro_span", issue = "54725")]
+    #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")]
     pub fn end(&self) -> Span {
         Span(self.0.end())
     }
@@ -527,7 +527,7 @@ impl Span {
     /// The one-indexed line of the source file where the span starts.
     ///
     /// To obtain the line of the span's end, use `span.end().line()`.
-    #[unstable(feature = "proc_macro_span", issue = "54725")]
+    #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")]
     pub fn line(&self) -> usize {
         self.0.line()
     }
@@ -535,7 +535,7 @@ impl Span {
     /// The one-indexed column of the source file where the span starts.
     ///
     /// To obtain the column of the span's end, use `span.end().column()`.
-    #[unstable(feature = "proc_macro_span", issue = "54725")]
+    #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")]
     pub fn column(&self) -> usize {
         self.0.column()
     }

From 2afb117f8f78e5a18609a4daf4f1e6204bd63360 Mon Sep 17 00:00:00 2001
From: Sergio Gasquez <sergio.gasquez@gmail.com>
Date: Thu, 24 Apr 2025 16:26:26 +0200
Subject: [PATCH 2/8] feat: Add XtensaAsmPrinter

---
 compiler/rustc_llvm/src/lib.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs
index 68058250a2671..ed5edeef1617d 100644
--- a/compiler/rustc_llvm/src/lib.rs
+++ b/compiler/rustc_llvm/src/lib.rs
@@ -229,6 +229,7 @@ pub fn initialize_available_targets() {
         LLVMInitializeXtensaTargetInfo,
         LLVMInitializeXtensaTarget,
         LLVMInitializeXtensaTargetMC,
+        LLVMInitializeXtensaAsmPrinter,
         LLVMInitializeXtensaAsmParser
     );
     init_target!(

From c07054bdd1f8927fef5b1665debff9cb27452cc0 Mon Sep 17 00:00:00 2001
From: Chris Denton <chris@chrisdenton.dev>
Date: Sun, 20 Apr 2025 15:29:17 +0000
Subject: [PATCH 3/8] Retry if creating temp fails with access denied

On Windows, if creating a temporary directory fails with permission denied then use a retry/backoff loop. This hopefully fixes a recuring error in our CI.
---
 Cargo.lock                                    |  4 +-
 .../rustc_codegen_ssa/src/back/archive.rs     |  4 +-
 compiler/rustc_codegen_ssa/src/back/link.rs   |  5 +-
 compiler/rustc_fs_util/Cargo.toml             |  1 +
 compiler/rustc_fs_util/src/lib.rs             | 46 ++++++++++++++++++-
 compiler/rustc_metadata/Cargo.toml            |  1 -
 compiler/rustc_metadata/src/fs.rs             |  4 +-
 7 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 97a90a44a398e..9aa32eea8ce2e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3740,6 +3740,9 @@ dependencies = [
 [[package]]
 name = "rustc_fs_util"
 version = "0.0.0"
+dependencies = [
+ "tempfile",
+]
 
 [[package]]
 name = "rustc_graphviz"
@@ -4052,7 +4055,6 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
- "tempfile",
  "tracing",
 ]
 
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs
index 34c84c64070d2..1e1bdfb5977af 100644
--- a/compiler/rustc_codegen_ssa/src/back/archive.rs
+++ b/compiler/rustc_codegen_ssa/src/back/archive.rs
@@ -13,9 +13,9 @@ use object::read::archive::ArchiveFile;
 use object::read::macho::FatArch;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::memmap::Mmap;
+use rustc_fs_util::TempDirBuilder;
 use rustc_session::Session;
 use rustc_span::Symbol;
-use tempfile::Builder as TempFileBuilder;
 use tracing::trace;
 
 use super::metadata::search_for_section;
@@ -501,7 +501,7 @@ impl<'a> ArArchiveBuilder<'a> {
         // it creates. We need it to be the default mode for back compat reasons however. (See
         // #107495) To handle this we are telling tempfile to create a temporary directory instead
         // and then inside this directory create a file using File::create.
-        let archive_tmpdir = TempFileBuilder::new()
+        let archive_tmpdir = TempDirBuilder::new()
             .suffix(".temp-archive")
             .tempdir_in(output.parent().unwrap_or_else(|| Path::new("")))
             .map_err(|err| {
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 8de68925cabbc..72850e35ced5a 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -18,7 +18,7 @@ use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
-use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
+use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize};
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_macros::LintDiagnostic;
 use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
@@ -48,7 +48,6 @@ use rustc_target::spec::{
     LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel,
     SanitizerSet, SplitDebuginfo,
 };
-use tempfile::Builder as TempFileBuilder;
 use tracing::{debug, info, warn};
 
 use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
@@ -100,7 +99,7 @@ pub fn link_binary(
         });
 
         if outputs.outputs.should_link() {
-            let tmpdir = TempFileBuilder::new()
+            let tmpdir = TempDirBuilder::new()
                 .prefix("rustc")
                 .tempdir()
                 .unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error }));
diff --git a/compiler/rustc_fs_util/Cargo.toml b/compiler/rustc_fs_util/Cargo.toml
index baca3bc7d49eb..90a6acade8b03 100644
--- a/compiler/rustc_fs_util/Cargo.toml
+++ b/compiler/rustc_fs_util/Cargo.toml
@@ -5,4 +5,5 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
+tempfile = "3.7.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs
index 0df1b243d697e..7a883a13b72da 100644
--- a/compiler/rustc_fs_util/src/lib.rs
+++ b/compiler/rustc_fs_util/src/lib.rs
@@ -1,6 +1,8 @@
-use std::ffi::CString;
+use std::ffi::{CString, OsStr};
 use std::path::{Path, PathBuf, absolute};
-use std::{fs, io};
+use std::{env, fs, io};
+
+use tempfile::TempDir;
 
 // Unfortunately, on windows, it looks like msvcrt.dll is silently translating
 // verbatim paths under the hood to non-verbatim paths! This manifests itself as
@@ -102,3 +104,43 @@ pub fn path_to_c_string(p: &Path) -> CString {
 pub fn try_canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
     fs::canonicalize(&path).or_else(|_| absolute(&path))
 }
+
+pub struct TempDirBuilder<'a, 'b> {
+    builder: tempfile::Builder<'a, 'b>,
+}
+
+impl<'a, 'b> TempDirBuilder<'a, 'b> {
+    pub fn new() -> Self {
+        Self { builder: tempfile::Builder::new() }
+    }
+
+    pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self {
+        self.builder.prefix(prefix);
+        self
+    }
+
+    pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self {
+        self.builder.suffix(suffix);
+        self
+    }
+
+    pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
+        let dir = dir.as_ref();
+        // On Windows in CI, we had been getting fairly frequent "Access is denied"
+        // errors when creating temporary directories.
+        // So this implements a simple retry with backoff loop.
+        #[cfg(windows)]
+        for wait in 1..11 {
+            match self.builder.tempdir_in(dir) {
+                Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {}
+                t => return t,
+            }
+            std::thread::sleep(std::time::Duration::from_millis(1 << wait));
+        }
+        self.builder.tempdir_in(dir)
+    }
+
+    pub fn tempdir(&self) -> io::Result<TempDir> {
+        self.tempdir_in(env::temp_dir())
+    }
+}
diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml
index 08dcc3d519a2b..b11f9260be7c0 100644
--- a/compiler/rustc_metadata/Cargo.toml
+++ b/compiler/rustc_metadata/Cargo.toml
@@ -26,7 +26,6 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
-tempfile = "3.2"
 tracing = "0.1"
 # tidy-alphabetical-end
 
diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs
index c4e1e0f1d1a99..e57534b847ef0 100644
--- a/compiler/rustc_metadata/src/fs.rs
+++ b/compiler/rustc_metadata/src/fs.rs
@@ -2,11 +2,11 @@ use std::path::{Path, PathBuf};
 use std::{fs, io};
 
 use rustc_data_structures::temp_dir::MaybeTempDir;
+use rustc_fs_util::TempDirBuilder;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::{CrateType, OutFileName, OutputType};
 use rustc_session::output::filename_for_metadata;
 use rustc_session::{MetadataKind, Session};
-use tempfile::Builder as TempFileBuilder;
 
 use crate::errors::{
     BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile,
@@ -45,7 +45,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
     // final destination, with an `fs::rename` call. In order for the rename to
     // always succeed, the temporary file needs to be on the same filesystem,
     // which is why we create it inside the output directory specifically.
-    let metadata_tmpdir = TempFileBuilder::new()
+    let metadata_tmpdir = TempDirBuilder::new()
         .prefix("rmeta")
         .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new("")))
         .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err }));

From f072d3074170f1e70a6f6a1b309fa43f62c25a3c Mon Sep 17 00:00:00 2001
From: Kivooeo <Kivooeo123@gmail.com>
Date: Fri, 25 Apr 2025 15:55:44 +0500
Subject: [PATCH 4/8] resolved conflict

---
 compiler/rustc_parse/messages.ftl             |  2 ++
 compiler/rustc_parse/src/errors.rs            |  7 +++++
 compiler/rustc_parse/src/parser/expr.rs       | 12 +++++---
 .../parser/or-in-let-chain.edition2021.stderr | 28 +++++++++++++++++++
 .../parser/or-in-let-chain.edition2024.stderr | 28 +++++++++++++++++++
 tests/ui/parser/or-in-let-chain.rs            | 17 +++++++++++
 .../ast-validate-guards.rs                    |  2 +-
 .../ast-validate-guards.stderr                |  9 +-----
 .../disallowed-positions.feature.stderr       | 18 ++----------
 .../disallowed-positions.no_feature.stderr    | 18 ++----------
 .../disallowed-positions.nothing.stderr       | 18 ++----------
 .../disallowed-positions.rs                   |  4 +--
 12 files changed, 100 insertions(+), 63 deletions(-)
 create mode 100644 tests/ui/parser/or-in-let-chain.edition2021.stderr
 create mode 100644 tests/ui/parser/or-in-let-chain.edition2024.stderr
 create mode 100644 tests/ui/parser/or-in-let-chain.rs

diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index ae9143486eea3..ac4f7ed64e22f 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -675,6 +675,8 @@ parse_note_pattern_alternatives_use_single_vert = alternatives in or-patterns ar
 
 parse_nul_in_c_str = null characters in C string literals are not supported
 
+parse_or_in_let_chain = `||` operators are not supported in let chain conditions
+
 parse_or_pattern_not_allowed_in_fn_parameters = top-level or-patterns are not allowed in function parameters
 parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allowed in `let` bindings
 parse_out_of_range_hex_escape = out of range hex escape
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 35cf4c1b00d4b..6a6fb0eb9b5ba 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -478,6 +478,13 @@ pub(crate) struct ExpectedExpressionFoundLet {
     pub comparison: Option<MaybeComparison>,
 }
 
+#[derive(Diagnostic)]
+#[diag(parse_or_in_let_chain)]
+pub(crate) struct OrInLetChain {
+    #[primary_span]
+    pub span: Span,
+}
+
 #[derive(Subdiagnostic, Clone, Copy)]
 #[multipart_suggestion(
     parse_maybe_missing_let,
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 370eb3f402d93..f3b53971b2957 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -4073,14 +4073,18 @@ impl MutVisitor for CondChecker<'_> {
         match e.kind {
             ExprKind::Let(_, _, _, ref mut recovered @ Recovered::No) => {
                 if let Some(reason) = self.forbid_let_reason {
-                    *recovered = Recovered::Yes(self.parser.dcx().emit_err(
-                        errors::ExpectedExpressionFoundLet {
+                    let error = match reason {
+                        NotSupportedOr(or_span) => {
+                            self.parser.dcx().emit_err(errors::OrInLetChain { span: or_span })
+                        }
+                        _ => self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet {
                             span,
                             reason,
                             missing_let: self.missing_let,
                             comparison: self.comparison,
-                        },
-                    ));
+                        }),
+                    };
+                    *recovered = Recovered::Yes(error);
                 } else if self.depth > 1 {
                     // Top level `let` is always allowed; only gate chains
                     match self.let_chains_policy {
diff --git a/tests/ui/parser/or-in-let-chain.edition2021.stderr b/tests/ui/parser/or-in-let-chain.edition2021.stderr
new file mode 100644
index 0000000000000..a97095cc3b82e
--- /dev/null
+++ b/tests/ui/parser/or-in-let-chain.edition2021.stderr
@@ -0,0 +1,28 @@
+error: `||` operators are not supported in let chain conditions
+  --> $DIR/or-in-let-chain.rs:6:24
+   |
+LL |     if let true = true || false {}
+   |                        ^^
+
+error: expected expression, found `let` statement
+  --> $DIR/or-in-let-chain.rs:9:9
+   |
+LL |     if (let true = true) || false {}
+   |         ^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: `||` operators are not supported in let chain conditions
+  --> $DIR/or-in-let-chain.rs:12:24
+   |
+LL |     if let true = true || false || true {}
+   |                        ^^
+
+error: `||` operators are not supported in let chain conditions
+  --> $DIR/or-in-let-chain.rs:15:33
+   |
+LL |     if let true = true && false || true {}
+   |                                 ^^
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/parser/or-in-let-chain.edition2024.stderr b/tests/ui/parser/or-in-let-chain.edition2024.stderr
new file mode 100644
index 0000000000000..a97095cc3b82e
--- /dev/null
+++ b/tests/ui/parser/or-in-let-chain.edition2024.stderr
@@ -0,0 +1,28 @@
+error: `||` operators are not supported in let chain conditions
+  --> $DIR/or-in-let-chain.rs:6:24
+   |
+LL |     if let true = true || false {}
+   |                        ^^
+
+error: expected expression, found `let` statement
+  --> $DIR/or-in-let-chain.rs:9:9
+   |
+LL |     if (let true = true) || false {}
+   |         ^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: `||` operators are not supported in let chain conditions
+  --> $DIR/or-in-let-chain.rs:12:24
+   |
+LL |     if let true = true || false || true {}
+   |                        ^^
+
+error: `||` operators are not supported in let chain conditions
+  --> $DIR/or-in-let-chain.rs:15:33
+   |
+LL |     if let true = true && false || true {}
+   |                                 ^^
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/parser/or-in-let-chain.rs b/tests/ui/parser/or-in-let-chain.rs
new file mode 100644
index 0000000000000..4c4372bb00f0f
--- /dev/null
+++ b/tests/ui/parser/or-in-let-chain.rs
@@ -0,0 +1,17 @@
+//@ revisions: edition2021 edition2024
+//@ [edition2021] edition: 2021
+//@ [edition2024] edition: 2024
+
+fn main() {
+    if let true = true || false {}
+    //~^ ERROR `||` operators are not supported in let chain conditions
+    // With parentheses
+    if (let true = true) || false {}
+    //~^ ERROR expected expression, found `let` statement
+    // Multiple || operators
+    if let true = true || false || true {}
+    //~^ ERROR `||` operators are not supported in let chain conditions
+    // Mixed operators (should still show error for ||)
+    if let true = true && false || true {}
+    //~^ ERROR `||` operators are not supported in let chain conditions
+}
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs
index ae13f7c76badd..ed461af66aaf9 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs
@@ -3,7 +3,7 @@
 fn let_or_guard(x: Result<Option<i32>, ()>) {
     match x {
         Ok(opt) if let Some(4) = opt || false  => {}
-        //~^ ERROR expected expression, found `let` statement
+        //~^ ERROR `||` operators are not supported in let chain conditions
         _ => {}
     }
 }
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr
index 4b85fdd505057..0566d96fddc31 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr
@@ -1,11 +1,4 @@
-error: expected expression, found `let` statement
-  --> $DIR/ast-validate-guards.rs:5:20
-   |
-LL |         Ok(opt) if let Some(4) = opt || false  => {}
-   |                    ^^^^^^^^^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
+error: `||` operators are not supported in let chain conditions
   --> $DIR/ast-validate-guards.rs:5:38
    |
 LL |         Ok(opt) if let Some(4) = opt || false  => {}
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr
index 817e226bc45d5..141a6d255d086 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr
@@ -272,14 +272,7 @@ LL |     if (let 0 = 0)? {}
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:121:16
-   |
-LL |     if true || let 0 = 0 {}
-   |                ^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
+error: `||` operators are not supported in let chain conditions
   --> $DIR/disallowed-positions.rs:121:13
    |
 LL |     if true || let 0 = 0 {}
@@ -485,14 +478,7 @@ LL |     while (let 0 = 0)? {}
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:212:19
-   |
-LL |     while true || let 0 = 0 {}
-   |                   ^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
+error: `||` operators are not supported in let chain conditions
   --> $DIR/disallowed-positions.rs:212:16
    |
 LL |     while true || let 0 = 0 {}
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr
index bab50c22c0308..dda09de4c5344 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr
@@ -272,14 +272,7 @@ LL |     if (let 0 = 0)? {}
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:121:16
-   |
-LL |     if true || let 0 = 0 {}
-   |                ^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
+error: `||` operators are not supported in let chain conditions
   --> $DIR/disallowed-positions.rs:121:13
    |
 LL |     if true || let 0 = 0 {}
@@ -485,14 +478,7 @@ LL |     while (let 0 = 0)? {}
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:212:19
-   |
-LL |     while true || let 0 = 0 {}
-   |                   ^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
+error: `||` operators are not supported in let chain conditions
   --> $DIR/disallowed-positions.rs:212:16
    |
 LL |     while true || let 0 = 0 {}
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr
index 943956feb4e07..5b53691cbf546 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr
@@ -272,14 +272,7 @@ LL |     if (let 0 = 0)? {}
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:121:16
-   |
-LL |     if true || let 0 = 0 {}
-   |                ^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
+error: `||` operators are not supported in let chain conditions
   --> $DIR/disallowed-positions.rs:121:13
    |
 LL |     if true || let 0 = 0 {}
@@ -485,14 +478,7 @@ LL |     while (let 0 = 0)? {}
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:212:19
-   |
-LL |     while true || let 0 = 0 {}
-   |                   ^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
+error: `||` operators are not supported in let chain conditions
   --> $DIR/disallowed-positions.rs:212:16
    |
 LL |     while true || let 0 = 0 {}
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs
index 0b0abe6ec1754..65beccf2214fd 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs
@@ -119,7 +119,7 @@ fn nested_within_if_expr() {
     //~^ ERROR expected expression, found `let` statement
 
     if true || let 0 = 0 {}
-    //~^ ERROR expected expression, found `let` statement
+    //~^ ERROR `||` operators are not supported in let chain conditions
     if (true || let 0 = 0) {}
     //~^ ERROR expected expression, found `let` statement
     if true && (true || let 0 = 0) {}
@@ -210,7 +210,7 @@ fn nested_within_while_expr() {
     //~^ ERROR expected expression, found `let` statement
 
     while true || let 0 = 0 {}
-    //~^ ERROR expected expression, found `let` statement
+    //~^ ERROR `||` operators are not supported in let chain conditions
     while (true || let 0 = 0) {}
     //~^ ERROR expected expression, found `let` statement
     while true && (true || let 0 = 0) {}

From 009db53e49923ced9f7b0dcbc58da47afb031177 Mon Sep 17 00:00:00 2001
From: lcnr <rust@lcnr.de>
Date: Fri, 25 Apr 2025 17:59:33 +0000
Subject: [PATCH 5/8] handle specialization in the new trait solver

uwu :3
---
 compiler/rustc_middle/src/ty/context.rs       |  4 +
 .../src/solve/assembly/mod.rs                 | 73 ++++++++++++++++++-
 .../src/solve/eval_ctxt/mod.rs                | 10 +--
 .../rustc_next_trait_solver/src/solve/mod.rs  | 11 +++
 .../src/solve/normalizes_to/mod.rs            | 27 +++++--
 .../src/solve/trait_goals.rs                  |  4 +-
 compiler/rustc_type_ir/src/interner.rs        |  2 +
 ...cializing-impl-over-default.current.stderr | 12 +++
 ...specializing-impl-over-default.next.stderr | 12 +++
 .../prefer-specializing-impl-over-default.rs  | 29 ++++++++
 ...ization-default-projection.current.stderr} |  6 +-
 ...cialization-default-projection.next.stderr | 43 +++++++++++
 .../specialization-default-projection.rs      |  4 +
 ...ecialization-default-types.current.stderr} |  6 +-
 .../specialization-default-types.next.stderr  | 39 ++++++++++
 .../specialization-default-types.rs           |  4 +
 .../next-solver/specialization-transmute.rs   |  8 +-
 .../specialization-transmute.stderr           | 37 ++++------
 .../specialization-unconstrained.rs           |  2 +-
 .../specialization-unconstrained.stderr       |  8 +-
 20 files changed, 290 insertions(+), 51 deletions(-)
 create mode 100644 tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr
 create mode 100644 tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr
 create mode 100644 tests/ui/specialization/prefer-specializing-impl-over-default.rs
 rename tests/ui/specialization/{specialization-default-projection.stderr => specialization-default-projection.current.stderr} (91%)
 create mode 100644 tests/ui/specialization/specialization-default-projection.next.stderr
 rename tests/ui/specialization/{specialization-default-types.stderr => specialization-default-types.current.stderr} (91%)
 create mode 100644 tests/ui/specialization/specialization-default-types.next.stderr

diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 98057a25f04c7..e8dad1e056cbd 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -590,6 +590,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.defaultness(def_id).has_value()
     }
 
+    fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool {
+        self.specializes((impl_def_id, victim_def_id))
+    }
+
     fn impl_is_default(self, impl_def_id: DefId) -> bool {
         self.defaultness(impl_def_id).is_default()
     }
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index ecb57cc0ad71b..381a732b8dee9 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -10,6 +10,7 @@ use rustc_type_ir::{
 };
 use tracing::{debug, instrument};
 
+use super::has_only_region_constraints;
 use super::trait_goals::TraitGoalProvenVia;
 use crate::delegate::SolverDelegate;
 use crate::solve::inspect::ProbeKind;
@@ -771,6 +772,69 @@ where
             }
         })
     }
+}
+
+pub(super) enum AllowInferenceConstraints {
+    Yes,
+    No,
+}
+
+impl<D, I> EvalCtxt<'_, D>
+where
+    D: SolverDelegate<Interner = I>,
+    I: Interner,
+{
+    /// Check whether we can ignore impl candidates due to specialization.
+    ///
+    /// This is only necessary for `feature(specialization)` and seems quite ugly.
+    pub(super) fn filter_specialized_impls(
+        &mut self,
+        allow_inference_constraints: AllowInferenceConstraints,
+        candidates: &mut Vec<Candidate<I>>,
+    ) {
+        match self.typing_mode() {
+            TypingMode::Coherence => return,
+            TypingMode::Analysis { .. }
+            | TypingMode::Borrowck { .. }
+            | TypingMode::PostBorrowckAnalysis { .. }
+            | TypingMode::PostAnalysis => {}
+        }
+
+        let mut i = 0;
+        'outer: while i < candidates.len() {
+            let CandidateSource::Impl(victim_def_id) = candidates[i].source else {
+                i += 1;
+                continue;
+            };
+
+            for (j, c) in candidates.iter().enumerate() {
+                if i == j {
+                    continue;
+                }
+
+                let CandidateSource::Impl(other_def_id) = c.source else {
+                    continue;
+                };
+
+                // See if we can toss out `victim` based on specialization.
+                //
+                // While this requires us to know *for sure* that the `lhs` impl applies
+                // we still use modulo regions here. This is fine as specialization currently
+                // assumes that specializing impls have to be always applicable, meaning that
+                // the only allowed region constraints may be constraints also present on the default impl.
+                if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes)
+                    || has_only_region_constraints(c.result)
+                {
+                    if self.cx().impl_specializes(other_def_id, victim_def_id) {
+                        candidates.remove(i);
+                        continue 'outer;
+                    }
+                }
+            }
+
+            i += 1;
+        }
+    }
 
     /// Assemble and merge candidates for goals which are related to an underlying trait
     /// goal. Right now, this is normalizes-to and host effect goals.
@@ -857,7 +921,7 @@ where
                 }
             }
             TraitGoalProvenVia::Misc => {
-                let candidates =
+                let mut candidates =
                     self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
 
                 // Prefer "orphaned" param-env normalization predicates, which are used
@@ -871,6 +935,13 @@ where
                     return Ok(response);
                 }
 
+                // We drop specialized impls to allow normalization via a final impl here. In case
+                // the specializing impl has different inference constraints from the specialized
+                // impl, proving the trait goal is already ambiguous, so we never get here. This
+                // means we can just ignore inference constraints and don't have to special-case
+                // constraining the normalized-to `term`.
+                self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);
+
                 let responses: Vec<_> = candidates.iter().map(|c| c.result).collect();
                 if let Some(response) = self.try_merge_responses(&responses) {
                     Ok(response)
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 27ca8787db5c5..19fb2b00fcb3a 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -16,6 +16,7 @@ use rustc_type_ir::{
 };
 use tracing::{instrument, trace};
 
+use super::has_only_region_constraints;
 use crate::coherence;
 use crate::delegate::SolverDelegate;
 use crate::solve::inspect::{self, ProofTreeBuilder};
@@ -476,13 +477,8 @@ where
             Ok(response) => response,
         };
 
-        let has_changed = if !response.value.var_values.is_identity_modulo_regions()
-            || !response.value.external_constraints.opaque_types.is_empty()
-        {
-            HasChanged::Yes
-        } else {
-            HasChanged::No
-        };
+        let has_changed =
+            if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No };
 
         let (normalization_nested_goals, certainty) =
             self.instantiate_and_apply_query_response(goal.param_env, orig_values, response);
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index 0695c5acdcab3..d9cf84787a036 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -70,6 +70,17 @@ fn has_no_inference_or_external_constraints<I: Interner>(
         && normalization_nested_goals.is_empty()
 }
 
+fn has_only_region_constraints<I: Interner>(response: ty::Canonical<I, Response<I>>) -> bool {
+    let ExternalConstraintsData {
+        region_constraints: _,
+        ref opaque_types,
+        ref normalization_nested_goals,
+    } = *response.value.external_constraints;
+    response.value.var_values.is_identity_modulo_regions()
+        && opaque_types.is_empty()
+        && normalization_nested_goals.is_empty()
+}
+
 impl<'a, D, I> EvalCtxt<'a, D>
 where
     D: SolverDelegate<Interner = I>,
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index 9466901683e21..119d197de134f 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -213,9 +213,6 @@ where
                 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             };
 
-            // In case the associated item is hidden due to specialization, we have to
-            // return ambiguity this would otherwise be incomplete, resulting in
-            // unsoundness during coherence (#105782).
             let target_item_def_id = match ecx.fetch_eligible_assoc_item(
                 goal_trait_ref,
                 goal.predicate.def_id(),
@@ -223,8 +220,28 @@ where
             ) {
                 Ok(Some(target_item_def_id)) => target_item_def_id,
                 Ok(None) => {
-                    return ecx
-                        .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+                    match ecx.typing_mode() {
+                        // In case the associated item is hidden due to specialization, we have to
+                        // return ambiguity this would otherwise be incomplete, resulting in
+                        // unsoundness during coherence (#105782).
+                        ty::TypingMode::Coherence => {
+                            return ecx.evaluate_added_goals_and_make_canonical_response(
+                                Certainty::AMBIGUOUS,
+                            );
+                        }
+                        // Outside of coherence, we treat the associated item as rigid instead.
+                        ty::TypingMode::Analysis { .. }
+                        | ty::TypingMode::Borrowck { .. }
+                        | ty::TypingMode::PostBorrowckAnalysis { .. }
+                        | ty::TypingMode::PostAnalysis => {
+                            ecx.structurally_instantiate_normalizes_to_term(
+                                goal,
+                                goal.predicate.alias,
+                            );
+                            return ecx
+                                .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+                        }
+                    };
                 }
                 Err(guar) => return error_response(ecx, guar),
             };
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 3404c47bba2a3..24657496df6a5 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -13,7 +13,7 @@ use tracing::{instrument, trace};
 
 use crate::delegate::SolverDelegate;
 use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
-use crate::solve::assembly::{self, AssembleCandidatesFrom, Candidate};
+use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate};
 use crate::solve::inspect::ProbeKind;
 use crate::solve::{
     BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
@@ -1338,6 +1338,8 @@ where
             };
         }
 
+        self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates);
+
         // If there are *only* global where bounds, then make sure to return that this
         // is still reported as being proven-via the param-env so that rigid projections
         // operate correctly. Otherwise, drop all global where-bounds before merging the
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index ab38556589e03..9758cecaf6ac7 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -286,6 +286,8 @@ pub trait Interner:
 
     fn has_item_definition(self, def_id: Self::DefId) -> bool;
 
+    fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool;
+
     fn impl_is_default(self, impl_def_id: Self::DefId) -> bool;
 
     fn impl_trait_ref(self, impl_def_id: Self::DefId) -> ty::EarlyBinder<Self, ty::TraitRef<Self>>;
diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr b/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr
new file mode 100644
index 0000000000000..7e3df0c83f9f3
--- /dev/null
+++ b/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr
@@ -0,0 +1,12 @@
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/prefer-specializing-impl-over-default.rs:5:12
+   |
+LL | #![feature(specialization)]
+   |            ^^^^^^^^^^^^^^
+   |
+   = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+   = help: consider using `min_specialization` instead, which is more stable and complete
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr b/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr
new file mode 100644
index 0000000000000..7e3df0c83f9f3
--- /dev/null
+++ b/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr
@@ -0,0 +1,12 @@
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/prefer-specializing-impl-over-default.rs:5:12
+   |
+LL | #![feature(specialization)]
+   |            ^^^^^^^^^^^^^^
+   |
+   = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+   = help: consider using `min_specialization` instead, which is more stable and complete
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.rs b/tests/ui/specialization/prefer-specializing-impl-over-default.rs
new file mode 100644
index 0000000000000..af6837b30cafa
--- /dev/null
+++ b/tests/ui/specialization/prefer-specializing-impl-over-default.rs
@@ -0,0 +1,29 @@
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+//@ check-pass
+#![feature(specialization)]
+//~^ WARN the feature `specialization` is incomplete
+
+trait WithAssoc: 'static {
+    type Assoc;
+}
+impl<T: 'static> WithAssoc for (T,) {
+    type Assoc = ();
+}
+
+struct GenericArray<U: WithAssoc>(U::Assoc);
+
+trait AbiExample {
+    fn example();
+}
+impl<U: WithAssoc> AbiExample for GenericArray<U> {
+    fn example() {}
+}
+impl<T> AbiExample for T {
+    default fn example() {}
+}
+
+fn main() {
+    let _ = GenericArray::<((),)>::example();
+}
diff --git a/tests/ui/specialization/specialization-default-projection.stderr b/tests/ui/specialization/specialization-default-projection.current.stderr
similarity index 91%
rename from tests/ui/specialization/specialization-default-projection.stderr
rename to tests/ui/specialization/specialization-default-projection.current.stderr
index b8b81876d8131..038c379c43e13 100644
--- a/tests/ui/specialization/specialization-default-projection.stderr
+++ b/tests/ui/specialization/specialization-default-projection.current.stderr
@@ -1,5 +1,5 @@
 warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/specialization-default-projection.rs:1:12
+  --> $DIR/specialization-default-projection.rs:5:12
    |
 LL | #![feature(specialization)]
    |            ^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | #![feature(specialization)]
    = note: `#[warn(incomplete_features)]` on by default
 
 error[E0308]: mismatched types
-  --> $DIR/specialization-default-projection.rs:21:5
+  --> $DIR/specialization-default-projection.rs:25:5
    |
 LL | fn generic<T>() -> <T as Foo>::Assoc {
    |                    ----------------- expected `<T as Foo>::Assoc` because of return type
@@ -23,7 +23,7 @@ LL |     ()
    = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
 
 error[E0308]: mismatched types
-  --> $DIR/specialization-default-projection.rs:28:5
+  --> $DIR/specialization-default-projection.rs:32:5
    |
 LL | fn monomorphic() -> () {
    |                     -- expected `()` because of return type
diff --git a/tests/ui/specialization/specialization-default-projection.next.stderr b/tests/ui/specialization/specialization-default-projection.next.stderr
new file mode 100644
index 0000000000000..9111f173a9c87
--- /dev/null
+++ b/tests/ui/specialization/specialization-default-projection.next.stderr
@@ -0,0 +1,43 @@
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/specialization-default-projection.rs:5:12
+   |
+LL | #![feature(specialization)]
+   |            ^^^^^^^^^^^^^^
+   |
+   = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+   = help: consider using `min_specialization` instead, which is more stable and complete
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0308]: mismatched types
+  --> $DIR/specialization-default-projection.rs:25:5
+   |
+LL | fn generic<T>() -> <T as Foo>::Assoc {
+   |                    ----------------- expected `<T as Foo>::Assoc` because of return type
+...
+LL |     ()
+   |     ^^ types differ
+   |
+   = note: expected associated type `<T as Foo>::Assoc`
+                    found unit type `()`
+   = help: consider constraining the associated type `<T as Foo>::Assoc` to `()` or calling a method that returns `<T as Foo>::Assoc`
+   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
+
+error[E0308]: mismatched types
+  --> $DIR/specialization-default-projection.rs:32:5
+   |
+LL | fn monomorphic() -> () {
+   |                     -- expected `()` because of return type
+...
+LL |     generic::<()>()
+   |     ^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;`
+   |     |
+   |     types differ
+   |
+   = note:    expected unit type `()`
+           found associated type `<() as Foo>::Assoc`
+   = help: consider constraining the associated type `<() as Foo>::Assoc` to `()`
+   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/specialization/specialization-default-projection.rs b/tests/ui/specialization/specialization-default-projection.rs
index 7f3ae951287ca..4f69ccb59746b 100644
--- a/tests/ui/specialization/specialization-default-projection.rs
+++ b/tests/ui/specialization/specialization-default-projection.rs
@@ -1,3 +1,7 @@
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+
 #![feature(specialization)] //~ WARN the feature `specialization` is incomplete
 
 // Make sure we can't project defaulted associated types
diff --git a/tests/ui/specialization/specialization-default-types.stderr b/tests/ui/specialization/specialization-default-types.current.stderr
similarity index 91%
rename from tests/ui/specialization/specialization-default-types.stderr
rename to tests/ui/specialization/specialization-default-types.current.stderr
index 774ac9536175d..67477f9a6d537 100644
--- a/tests/ui/specialization/specialization-default-types.stderr
+++ b/tests/ui/specialization/specialization-default-types.current.stderr
@@ -1,5 +1,5 @@
 warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/specialization-default-types.rs:5:12
+  --> $DIR/specialization-default-types.rs:9:12
    |
 LL | #![feature(specialization)]
    |            ^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | #![feature(specialization)]
    = note: `#[warn(incomplete_features)]` on by default
 
 error[E0308]: mismatched types
-  --> $DIR/specialization-default-types.rs:15:9
+  --> $DIR/specialization-default-types.rs:19:9
    |
 LL |     default type Output = Box<T>;
    |     ----------------------------- associated type is `default` and may be overridden
@@ -22,7 +22,7 @@ LL |         Box::new(self)
                        found struct `Box<T>`
 
 error[E0308]: mismatched types
-  --> $DIR/specialization-default-types.rs:25:5
+  --> $DIR/specialization-default-types.rs:29:5
    |
 LL | fn trouble<T>(t: T) -> Box<T> {
    |                        ------ expected `Box<T>` because of return type
diff --git a/tests/ui/specialization/specialization-default-types.next.stderr b/tests/ui/specialization/specialization-default-types.next.stderr
new file mode 100644
index 0000000000000..4f7c47654460e
--- /dev/null
+++ b/tests/ui/specialization/specialization-default-types.next.stderr
@@ -0,0 +1,39 @@
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/specialization-default-types.rs:9:12
+   |
+LL | #![feature(specialization)]
+   |            ^^^^^^^^^^^^^^
+   |
+   = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+   = help: consider using `min_specialization` instead, which is more stable and complete
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0308]: mismatched types
+  --> $DIR/specialization-default-types.rs:19:9
+   |
+LL |     default type Output = Box<T>;
+   |     ----------------------------- associated type is `default` and may be overridden
+LL |     default fn generate(self) -> Self::Output {
+   |                                  ------------ expected `<T as Example>::Output` because of return type
+LL |         Box::new(self)
+   |         ^^^^^^^^^^^^^^ types differ
+   |
+   = note: expected associated type `<T as Example>::Output`
+                       found struct `Box<T>`
+
+error[E0308]: mismatched types
+  --> $DIR/specialization-default-types.rs:29:5
+   |
+LL | fn trouble<T>(t: T) -> Box<T> {
+   |                        ------ expected `Box<T>` because of return type
+LL |     Example::generate(t)
+   |     ^^^^^^^^^^^^^^^^^^^^ types differ
+   |
+   = note:       expected struct `Box<T>`
+           found associated type `<T as Example>::Output`
+   = help: consider constraining the associated type `<T as Example>::Output` to `Box<T>`
+   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/specialization/specialization-default-types.rs b/tests/ui/specialization/specialization-default-types.rs
index 346471f11e4a8..77817abea127c 100644
--- a/tests/ui/specialization/specialization-default-types.rs
+++ b/tests/ui/specialization/specialization-default-types.rs
@@ -1,3 +1,7 @@
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+
 // It should not be possible to use the concrete value of a defaulted
 // associated type in the impl defining it -- otherwise, what happens
 // if it's overridden?
diff --git a/tests/ui/traits/next-solver/specialization-transmute.rs b/tests/ui/traits/next-solver/specialization-transmute.rs
index 376fa22ae1923..f1447cd6a9e2e 100644
--- a/tests/ui/traits/next-solver/specialization-transmute.rs
+++ b/tests/ui/traits/next-solver/specialization-transmute.rs
@@ -10,11 +10,8 @@ trait Default {
 
 impl<T> Default for T {
     default type Id = T;
-    // This will be fixed by #111994
     fn intu(&self) -> &Self::Id {
-        //~^ ERROR type annotations needed
-        //~| ERROR cannot normalize `<T as Default>::Id: '_`
-        self //~ ERROR cannot satisfy
+        self //~ ERROR mismatched types
     }
 }
 
@@ -25,6 +22,7 @@ fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {
 use std::num::NonZero;
 
 fn main() {
-    let s = transmute::<u8, Option<NonZero<u8>>>(0); //~ ERROR cannot satisfy
+    let s = transmute::<u8, Option<NonZero<u8>>>(0);
+    //~^ ERROR type mismatch resolving `<u8 as Default>::Id == Option<NonZero<u8>>`
     assert_eq!(s, None);
 }
diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr
index eeb101911c435..8bd290ea19707 100644
--- a/tests/ui/traits/next-solver/specialization-transmute.stderr
+++ b/tests/ui/traits/next-solver/specialization-transmute.stderr
@@ -8,37 +8,32 @@ LL | #![feature(specialization)]
    = help: consider using `min_specialization` instead, which is more stable and complete
    = note: `#[warn(incomplete_features)]` on by default
 
-error: cannot normalize `<T as Default>::Id: '_`
-  --> $DIR/specialization-transmute.rs:14:5
+error[E0308]: mismatched types
+  --> $DIR/specialization-transmute.rs:14:9
    |
 LL |     fn intu(&self) -> &Self::Id {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0282]: type annotations needed
-  --> $DIR/specialization-transmute.rs:14:23
-   |
-LL |     fn intu(&self) -> &Self::Id {
-   |                       ^^^^^^^^^ cannot infer type for reference `&<T as Default>::Id`
-
-error[E0284]: type annotations needed: cannot satisfy `<T as Default>::Id normalizes-to T`
-  --> $DIR/specialization-transmute.rs:17:9
-   |
+   |                       --------- expected `&<T as Default>::Id` because of return type
 LL |         self
-   |         ^^^^ cannot satisfy `<T as Default>::Id normalizes-to T`
+   |         ^^^^ types differ
+   |
+   = note: expected reference `&<T as Default>::Id`
+              found reference `&T`
 
-error[E0284]: type annotations needed: cannot satisfy `<u8 as Default>::Id normalizes-to Option<NonZero<u8>>`
-  --> $DIR/specialization-transmute.rs:28:13
+error[E0271]: type mismatch resolving `<u8 as Default>::Id == Option<NonZero<u8>>`
+  --> $DIR/specialization-transmute.rs:25:50
    |
 LL |     let s = transmute::<u8, Option<NonZero<u8>>>(0);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<u8 as Default>::Id normalizes-to Option<NonZero<u8>>`
+   |             ------------------------------------ ^ types differ
+   |             |
+   |             required by a bound introduced by this call
    |
 note: required by a bound in `transmute`
-  --> $DIR/specialization-transmute.rs:21:25
+  --> $DIR/specialization-transmute.rs:18:25
    |
 LL | fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {
    |                         ^^^^^^ required by this bound in `transmute`
 
-error: aborting due to 4 previous errors; 1 warning emitted
+error: aborting due to 2 previous errors; 1 warning emitted
 
-Some errors have detailed explanations: E0282, E0284.
-For more information about an error, try `rustc --explain E0282`.
+Some errors have detailed explanations: E0271, E0308.
+For more information about an error, try `rustc --explain E0271`.
diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.rs b/tests/ui/traits/next-solver/specialization-unconstrained.rs
index 2bbe784011071..6835c0764d6c8 100644
--- a/tests/ui/traits/next-solver/specialization-unconstrained.rs
+++ b/tests/ui/traits/next-solver/specialization-unconstrained.rs
@@ -18,5 +18,5 @@ fn test<T: Default<Id = U>, U>() {}
 
 fn main() {
     test::<u32, ()>();
-    //~^ ERROR cannot satisfy `<u32 as Default>::Id normalizes-to ()`
+    //~^ ERROR type mismatch resolving `<u32 as Default>::Id == ()`
 }
diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.stderr b/tests/ui/traits/next-solver/specialization-unconstrained.stderr
index e1d785b554bd3..1bcf5eddb5bca 100644
--- a/tests/ui/traits/next-solver/specialization-unconstrained.stderr
+++ b/tests/ui/traits/next-solver/specialization-unconstrained.stderr
@@ -8,11 +8,11 @@ LL | #![feature(specialization)]
    = help: consider using `min_specialization` instead, which is more stable and complete
    = note: `#[warn(incomplete_features)]` on by default
 
-error[E0284]: type annotations needed: cannot satisfy `<u32 as Default>::Id normalizes-to ()`
-  --> $DIR/specialization-unconstrained.rs:20:5
+error[E0271]: type mismatch resolving `<u32 as Default>::Id == ()`
+  --> $DIR/specialization-unconstrained.rs:20:12
    |
 LL |     test::<u32, ()>();
-   |     ^^^^^^^^^^^^^^^^^ cannot satisfy `<u32 as Default>::Id normalizes-to ()`
+   |            ^^^ types differ
    |
 note: required by a bound in `test`
   --> $DIR/specialization-unconstrained.rs:17:20
@@ -22,4 +22,4 @@ LL | fn test<T: Default<Id = U>, U>() {}
 
 error: aborting due to 1 previous error; 1 warning emitted
 
-For more information about this error, try `rustc --explain E0284`.
+For more information about this error, try `rustc --explain E0271`.

From 31c8d10342bfa437d23eb4aa3f9c807a67f14272 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 25 Apr 2025 17:57:59 +0000
Subject: [PATCH 6/8] Track per-obligation recursion depth only if there is
 inference

---
 .../src/solve/fulfill.rs                      | 23 +++++++++-----
 tests/ui/traits/next-solver/coerce-depth.rs   | 31 +++++++++++++++++++
 2 files changed, 46 insertions(+), 8 deletions(-)
 create mode 100644 tests/ui/traits/next-solver/coerce-depth.rs

diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 848d0646d009f..1f4fa5aac102a 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -163,15 +163,15 @@ where
     fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
         assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
         let mut errors = Vec::new();
-        for i in 0.. {
-            if !infcx.tcx.recursion_limit().value_within_limit(i) {
-                self.obligations.on_fulfillment_overflow(infcx);
-                // Only return true errors that we have accumulated while processing.
-                return errors;
-            }
-
+        loop {
             let mut has_changed = false;
-            for obligation in self.obligations.drain_pending(|_| true) {
+            for mut obligation in self.obligations.drain_pending(|_| true) {
+                if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
+                    self.obligations.on_fulfillment_overflow(infcx);
+                    // Only return true errors that we have accumulated while processing.
+                    return errors;
+                }
+
                 let goal = obligation.as_goal();
                 let result = <&SolverDelegate<'tcx>>::from(infcx)
                     .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span)
@@ -189,6 +189,13 @@ where
                 };
 
                 if changed == HasChanged::Yes {
+                    // We increment the recursion depth here to track the number of times
+                    // this goal has resulted in inference progress. This doesn't precisely
+                    // model the way that we track recursion depth in the old solver due
+                    // to the fact that we only process root obligations, but it is a good
+                    // approximation and should only result in fulfillment overflow in
+                    // pathological cases.
+                    obligation.recursion_depth += 1;
                     has_changed = true;
                 }
 
diff --git a/tests/ui/traits/next-solver/coerce-depth.rs b/tests/ui/traits/next-solver/coerce-depth.rs
new file mode 100644
index 0000000000000..c8fc3fcab59d8
--- /dev/null
+++ b/tests/ui/traits/next-solver/coerce-depth.rs
@@ -0,0 +1,31 @@
+//@ check-pass
+//@ compile-flags: -Znext-solver
+
+// Ensure that a stack of coerce predicates doesn't end up overflowing when they get procesed
+// in *reverse* order, which may require O(N) iterations of the fulfillment loop.
+
+#![recursion_limit = "16"]
+
+fn main() {
+    match 0 {
+        0 => None,
+        1 => None,
+        2 => None,
+        3 => None,
+        4 => None,
+        5 => None,
+        6 => None,
+        7 => None,
+        8 => None,
+        9 => None,
+        10 => None,
+        11 => None,
+        12 => None,
+        13 => None,
+        14 => None,
+        15 => None,
+        16 => None,
+        17 => None,
+        _ => Some(1u32),
+    };
+}

From ee9bdcabe530e9bec17d5beb12c046ce34a4cc29 Mon Sep 17 00:00:00 2001
From: lcnr <rust@lcnr.de>
Date: Fri, 25 Apr 2025 18:40:27 +0000
Subject: [PATCH 7/8] add regression test

---
 .../coroutine/dont-drop-stalled-generators.rs | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 tests/ui/coroutine/dont-drop-stalled-generators.rs

diff --git a/tests/ui/coroutine/dont-drop-stalled-generators.rs b/tests/ui/coroutine/dont-drop-stalled-generators.rs
new file mode 100644
index 0000000000000..8e0c45a9773b3
--- /dev/null
+++ b/tests/ui/coroutine/dont-drop-stalled-generators.rs
@@ -0,0 +1,25 @@
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+//@ check-pass
+//@ edition: 2024
+
+// This test previously used the `is_copy_raw` query during
+// HIR typeck, dropping the list of generators from the current
+// body. This then caused a query cycle.
+
+struct W<T>(*const T);
+
+impl<T: Send> Clone for W<T> {
+    fn clone(&self) -> Self { W(self.0) }
+}
+
+impl<T: Send> Copy for W<T> {}
+
+fn main() {
+    let coro = async {};
+    let x = W(&raw const coro);
+    let c = || {
+        let x = x;
+    };
+}

From 1862feb0e7b88487e17d999ee5d1eb97a0b27b6a Mon Sep 17 00:00:00 2001
From: Tobias <206408826+t5kd@users.noreply.github.com>
Date: Wed, 23 Apr 2025 20:33:22 +0200
Subject: [PATCH 8/8] Update `extern` docs for Rust 2024 and add safety remarks

---
 library/std/src/keyword_docs.rs | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs
index c07c391892d80..69c16f2eba47f 100644
--- a/library/std/src/keyword_docs.rs
+++ b/library/std/src/keyword_docs.rs
@@ -381,11 +381,15 @@ mod enum_keyword {}
 /// lazy_static;`. The other use is in foreign function interfaces (FFI).
 ///
 /// `extern` is used in two different contexts within FFI. The first is in the form of external
-/// blocks, for declaring function interfaces that Rust code can call foreign code by.
+/// blocks, for declaring function interfaces that Rust code can call foreign code by. This use
+/// of `extern` is unsafe, since we are asserting to the compiler that all function declarations
+/// are correct. If they are not, using these items may lead to undefined behavior.
 ///
 /// ```rust ignore
+/// // SAFETY: The function declarations given below are in
+/// // line with the header files of `my_c_library`.
 /// #[link(name = "my_c_library")]
-/// extern "C" {
+/// unsafe extern "C" {
 ///     fn my_c_function(x: i32) -> bool;
 /// }
 /// ```