diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index dc58d99ed9d74..9316516552446 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -636,6 +636,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)))
                     if traits.contains(&trait_pred.def_id()) =>
                 {
+                    let trait_pred = self.resolve_vars_if_possible(trait_pred);
                     if unsize_did == trait_pred.def_id() {
                         let self_ty = trait_pred.self_ty();
                         let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty();
@@ -662,7 +663,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 // Uncertain or unimplemented.
                 Ok(None) => {
                     if trait_pred.def_id() == unsize_did {
-                        let trait_pred = self.resolve_vars_if_possible(trait_pred);
                         let self_ty = trait_pred.self_ty();
                         let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty();
                         debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred);
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index c43a02724773a..eb3d67e720f2d 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -667,6 +667,7 @@ LLVMRustOptimize(
     assert(!PGOUsePath && !PGOSampleUsePath);
     PGOOpt = PGOOptions(PGOGenPath, "", "",
 #if LLVM_VERSION_GE(17, 0)
+                        "",
                         FS,
 #endif
                         PGOOptions::IRInstr, PGOOptions::NoCSAction,
@@ -675,6 +676,7 @@ LLVMRustOptimize(
     assert(!PGOSampleUsePath);
     PGOOpt = PGOOptions(PGOUsePath, "", "",
 #if LLVM_VERSION_GE(17, 0)
+                        "",
                         FS,
 #endif
                         PGOOptions::IRUse, PGOOptions::NoCSAction,
@@ -682,6 +684,7 @@ LLVMRustOptimize(
   } else if (PGOSampleUsePath) {
     PGOOpt = PGOOptions(PGOSampleUsePath, "", "",
 #if LLVM_VERSION_GE(17, 0)
+                        "",
                         FS,
 #endif
                         PGOOptions::SampleUse, PGOOptions::NoCSAction,
@@ -689,6 +692,7 @@ LLVMRustOptimize(
   } else if (DebugInfoForProfiling) {
     PGOOpt = PGOOptions("", "", "",
 #if LLVM_VERSION_GE(17, 0)
+                        "",
                         FS,
 #endif
                         PGOOptions::NoAction, PGOOptions::NoCSAction,
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index f22c620021eeb..7fb31df84d003 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -103,12 +103,9 @@ impl<'tcx> Tables<'tcx> {
             ty::Ref(_, _, _) => todo!(),
             ty::FnDef(_, _) => todo!(),
             ty::FnPtr(_) => todo!(),
-            ty::Placeholder(..) => todo!(),
             ty::Dynamic(_, _, _) => todo!(),
             ty::Closure(_, _) => todo!(),
             ty::Generator(_, _, _) => todo!(),
-            ty::GeneratorWitness(_) => todo!(),
-            ty::GeneratorWitnessMIR(_, _) => todo!(),
             ty::Never => todo!(),
             ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple(
                 fields.iter().map(|ty| self.intern_ty(ty)).collect(),
@@ -116,8 +113,13 @@ impl<'tcx> Tables<'tcx> {
             ty::Alias(_, _) => todo!(),
             ty::Param(_) => todo!(),
             ty::Bound(_, _) => todo!(),
-            ty::Infer(_) => todo!(),
-            ty::Error(_) => todo!(),
+            ty::Placeholder(..)
+            | ty::GeneratorWitness(_)
+            | ty::GeneratorWitnessMIR(_, _)
+            | ty::Infer(_)
+            | ty::Error(_) => {
+                unreachable!();
+            }
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
index bf6cbef8c3b1d..f40202cf2c59e 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -1,5 +1,6 @@
 use std::ops::ControlFlow;
 
+use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
 use rustc_infer::traits::util::supertraits;
@@ -11,7 +12,7 @@ use rustc_middle::traits::{
     ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
     ObligationCause, SelectionError,
 };
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::DUMMY_SP;
 
 use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource};
@@ -113,6 +114,12 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
                 ),
             ) => rematch_object(self, goal, nested_obligations),
 
+            (Certainty::Maybe(_), CandidateSource::BuiltinImpl(BuiltinImplSource::Misc))
+                if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
+            {
+                rematch_unsize(self, goal, nested_obligations)
+            }
+
             // Technically some builtin impls have nested obligations, but if
             // `Certainty::Yes`, then they should've all been verified and don't
             // need re-checking.
@@ -232,6 +239,9 @@ fn rematch_object<'tcx>(
     {
         assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*");
         if let ty::Dynamic(data, _, ty::Dyn) = goal.predicate.trait_ref.substs.type_at(1).kind() {
+            // FIXME: We also need to ensure that the source lifetime outlives the
+            // target lifetime. This doesn't matter for codegen, though, and only
+            // *really* matters if the goal's certainty is ambiguous.
             (true, data.principal().unwrap().with_self_ty(infcx.tcx, self_ty))
         } else {
             bug!()
@@ -305,3 +315,136 @@ fn rematch_object<'tcx>(
         ImplSource::Object(ImplSourceObjectData { vtable_base, nested })
     }))
 }
+
+/// The `Unsize` trait is particularly important to coercion, so we try rematch it.
+/// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait
+/// goal assembly in the solver, both for soundness and in order to avoid ICEs.
+fn rematch_unsize<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
+    mut nested: Vec<PredicateObligation<'tcx>>,
+) -> SelectionResult<'tcx, Selection<'tcx>> {
+    let tcx = infcx.tcx;
+    let a_ty = goal.predicate.self_ty();
+    let b_ty = goal.predicate.trait_ref.substs.type_at(1);
+
+    match (a_ty.kind(), b_ty.kind()) {
+        (_, &ty::Dynamic(data, region, ty::Dyn)) => {
+            // Check that the type implements all of the predicates of the def-id.
+            // (i.e. the principal, all of the associated types match, and any auto traits)
+            nested.extend(data.iter().map(|pred| {
+                Obligation::new(
+                    infcx.tcx,
+                    ObligationCause::dummy(),
+                    goal.param_env,
+                    pred.with_self_ty(tcx, a_ty),
+                )
+            }));
+            // The type must be Sized to be unsized.
+            let sized_def_id = tcx.require_lang_item(hir::LangItem::Sized, None);
+            nested.push(Obligation::new(
+                infcx.tcx,
+                ObligationCause::dummy(),
+                goal.param_env,
+                ty::TraitRef::new(tcx, sized_def_id, [a_ty]),
+            ));
+            // The type must outlive the lifetime of the `dyn` we're unsizing into.
+            nested.push(Obligation::new(
+                infcx.tcx,
+                ObligationCause::dummy(),
+                goal.param_env,
+                ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)),
+            ));
+        }
+        // `[T; n]` -> `[T]` unsizing
+        (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
+            nested.extend(
+                infcx
+                    .at(&ObligationCause::dummy(), goal.param_env)
+                    .eq(DefineOpaqueTypes::No, a_elem_ty, b_elem_ty)
+                    .expect("expected rematch to succeed")
+                    .into_obligations(),
+            );
+        }
+        // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
+        (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs))
+            if a_def.is_struct() && a_def.did() == b_def.did() =>
+        {
+            let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
+            // We must be unsizing some type parameters. This also implies
+            // that the struct has a tail field.
+            if unsizing_params.is_empty() {
+                bug!("expected rematch to succeed")
+            }
+
+            let tail_field = a_def
+                .non_enum_variant()
+                .fields
+                .raw
+                .last()
+                .expect("expected unsized ADT to have a tail field");
+            let tail_field_ty = tcx.type_of(tail_field.did);
+
+            let a_tail_ty = tail_field_ty.subst(tcx, a_substs);
+            let b_tail_ty = tail_field_ty.subst(tcx, b_substs);
+
+            // Substitute just the unsizing params from B into A. The type after
+            // this substitution must be equal to B. This is so we don't unsize
+            // unrelated type parameters.
+            let new_a_substs =
+                tcx.mk_substs_from_iter(a_substs.iter().enumerate().map(|(i, a)| {
+                    if unsizing_params.contains(i as u32) { b_substs[i] } else { a }
+                }));
+            let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_substs);
+
+            nested.extend(
+                infcx
+                    .at(&ObligationCause::dummy(), goal.param_env)
+                    .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty)
+                    .expect("expected rematch to succeed")
+                    .into_obligations(),
+            );
+
+            // Finally, we require that `TailA: Unsize<TailB>` for the tail field
+            // types.
+            nested.push(Obligation::new(
+                tcx,
+                ObligationCause::dummy(),
+                goal.param_env,
+                ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
+            ));
+        }
+        // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
+        (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
+            if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
+        {
+            let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
+            let b_last_ty = b_tys.last().unwrap();
+
+            // Substitute just the tail field of B., and require that they're equal.
+            let unsized_a_ty =
+                Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied());
+            nested.extend(
+                infcx
+                    .at(&ObligationCause::dummy(), goal.param_env)
+                    .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty)
+                    .expect("expected rematch to succeed")
+                    .into_obligations(),
+            );
+
+            // Similar to ADTs, require that the rest of the fields are equal.
+            nested.push(Obligation::new(
+                tcx,
+                ObligationCause::dummy(),
+                goal.param_env,
+                ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
+            ));
+        }
+        // FIXME: We *could* ICE here if either:
+        // 1. the certainty is `Certainty::Yes`,
+        // 2. we're in codegen (which should mean `Certainty::Yes`).
+        _ => return Ok(None),
+    }
+
+    Ok(Some(ImplSource::Builtin(nested)))
+}
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index 149350e62a014..e5a710c0a9646 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -256,7 +256,7 @@ def default_build_triple(verbose):
     if uname is None:
         return 'x86_64-pc-windows-msvc'
 
-    kernel, cputype, processor = uname.decode(default_encoding).split()
+    kernel, cputype, processor = uname.decode(default_encoding).split(maxsplit=2)
 
     # The goal here is to come up with the same triple as LLVM would,
     # at least for the subset of platforms we're willing to target.
diff --git a/src/tools/cargo b/src/tools/cargo
index 45782b6b8afd1..694a579566a9a 160000
--- a/src/tools/cargo
+++ b/src/tools/cargo
@@ -1 +1 @@
-Subproject commit 45782b6b8afd1da042d45c2daeec9c0744f72cc7
+Subproject commit 694a579566a9a1482b20aff8a68f0e4edd99bd28
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index ad10c3e07cef4..994156bdfd7b1 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -541,16 +541,15 @@ impl TestProps {
     }
 
     fn update_pass_mode(&mut self, ln: &str, revision: Option<&str>, config: &Config) {
-        let check_no_run = |s| {
-            if config.mode != Mode::Ui && config.mode != Mode::Incremental {
-                panic!("`{}` header is only supported in UI and incremental tests", s);
-            }
-            if config.mode == Mode::Incremental
-                && !revision.map_or(false, |r| r.starts_with("cfail"))
-                && !self.revisions.iter().all(|r| r.starts_with("cfail"))
-            {
-                panic!("`{}` header is only supported in `cfail` incremental tests", s);
+        let check_no_run = |s| match (config.mode, s) {
+            (Mode::Ui, _) => (),
+            (Mode::Codegen, "build-pass") => (),
+            (Mode::Incremental, _) => {
+                if revision.is_some() && !self.revisions.iter().all(|r| r.starts_with("cfail")) {
+                    panic!("`{s}` header is only supported in `cfail` incremental tests")
+                }
             }
+            (mode, _) => panic!("`{s}` header is not supported in `{mode}` tests"),
         };
         let pass_mode = if config.parse_name_directive(ln, "check-pass") {
             check_no_run("check-pass");
@@ -559,9 +558,7 @@ impl TestProps {
             check_no_run("build-pass");
             Some(PassMode::Build)
         } else if config.parse_name_directive(ln, "run-pass") {
-            if config.mode != Mode::Ui {
-                panic!("`run-pass` header is only supported in UI tests")
-            }
+            check_no_run("run-pass");
             Some(PassMode::Run)
         } else {
             None
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index ac19fe078f09e..b91e1b09330e1 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2752,6 +2752,10 @@ impl<'test> TestCx<'test> {
             self.fatal_proc_rec("compilation failed!", &proc_res);
         }
 
+        if let Some(PassMode::Build) = self.pass_mode() {
+            return;
+        }
+
         let output_path = self.output_base_name().with_extension("ll");
         let proc_res = self.verify_with_filecheck(&output_path);
         if !proc_res.status.success() {
diff --git a/src/tools/jsondocck/src/cache.rs b/src/tools/jsondocck/src/cache.rs
index f9e54232750b6..50697d46b8c70 100644
--- a/src/tools/jsondocck/src/cache.rs
+++ b/src/tools/jsondocck/src/cache.rs
@@ -15,8 +15,10 @@ impl Cache {
     /// Create a new cache, used to read files only once and otherwise store their contents.
     pub fn new(config: &Config) -> Cache {
         let root = Path::new(&config.doc_dir);
-        let filename = Path::new(&config.template).file_stem().unwrap();
-        let file_path = root.join(&Path::with_extension(Path::new(filename), "json"));
+        // `filename` needs to replace `-` with `_` to be sure the JSON path will always be valid.
+        let filename =
+            Path::new(&config.template).file_stem().unwrap().to_str().unwrap().replace('-', "_");
+        let file_path = root.join(&Path::with_extension(Path::new(&filename), "json"));
         let content = fs::read_to_string(&file_path).expect("failed to read JSON file");
 
         Cache {
diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs
index ee163ddfdd9a8..aaaba78cb46d1 100644
--- a/src/tools/jsondoclint/src/main.rs
+++ b/src/tools/jsondoclint/src/main.rs
@@ -1,4 +1,5 @@
 use std::io::{BufWriter, Write};
+use std::path::{Path, PathBuf};
 
 use anyhow::{bail, Result};
 use clap::Parser;
@@ -25,7 +26,7 @@ enum ErrorKind {
 
 #[derive(Debug, Serialize)]
 struct JsonOutput {
-    path: String,
+    path: PathBuf,
     errors: Vec<Error>,
 }
 
@@ -45,6 +46,12 @@ struct Cli {
 fn main() -> Result<()> {
     let Cli { path, verbose, json_output } = Cli::parse();
 
+    // We convert `-` into `_` for the file name to be sure the JSON path will always be correct.
+    let path = Path::new(&path);
+    let filename = path.file_name().unwrap().to_str().unwrap().replace('-', "_");
+    let parent = path.parent().unwrap();
+    let path = parent.join(&filename);
+
     let contents = fs::read_to_string(&path)?;
     let krate: Crate = serde_json::from_str(&contents)?;
     assert_eq!(krate.format_version, FORMAT_VERSION);
@@ -101,7 +108,7 @@ fn main() -> Result<()> {
                 ErrorKind::Custom(msg) => eprintln!("{}: {}", err.id.0, msg),
             }
         }
-        bail!("Errors validating json {path}");
+        bail!("Errors validating json {}", path.display());
     }
 
     Ok(())
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 2c237c418b1ad..9f0f0d86c8b29 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -56,6 +56,9 @@ const EXCEPTIONS_CARGO: &[(&str, &str)] = &[
     // tidy-alphabetical-start
     ("bitmaps", "MPL-2.0+"),
     ("bytesize", "Apache-2.0"),
+    ("ciborium", "Apache-2.0"),
+    ("ciborium-io", "Apache-2.0"),
+    ("ciborium-ll", "Apache-2.0"),
     ("dunce", "CC0-1.0 OR MIT-0 OR Apache-2.0"),
     ("fiat-crypto", "MIT OR Apache-2.0 OR BSD-1-Clause"),
     ("im-rc", "MPL-2.0+"),
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 55bf38110a6d5..c3a639528413b 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -10,7 +10,7 @@ use std::path::{Path, PathBuf};
 
 const ENTRY_LIMIT: usize = 900;
 // FIXME: The following limits should be reduced eventually.
-const ISSUES_ENTRY_LIMIT: usize = 1896;
+const ISSUES_ENTRY_LIMIT: usize = 1894;
 const ROOT_ENTRY_LIMIT: usize = 870;
 
 const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
diff --git a/tests/codegen/simd-wide-sum.rs b/tests/codegen/simd-wide-sum.rs
index db2aa20bde7ab..3116f9597bcdb 100644
--- a/tests/codegen/simd-wide-sum.rs
+++ b/tests/codegen/simd-wide-sum.rs
@@ -1,6 +1,11 @@
+// revisions: llvm mir-opt3
 // compile-flags: -C opt-level=3 -Z merge-functions=disabled --edition=2021
 // only-x86_64
 // ignore-debug: the debug assertions get in the way
+// [mir-opt3]compile-flags: -Zmir-opt-level=3
+// [mir-opt3]build-pass
+
+// mir-opt3 is a regression test for https://github.com/rust-lang/rust/issues/98016
 
 #![crate_type = "lib"]
 #![feature(portable_simd)]
@@ -47,9 +52,8 @@ pub fn wider_reduce_iter(x: Simd<u8, N>) -> u16 {
 #[no_mangle]
 // CHECK-LABEL: @wider_reduce_into_iter
 pub fn wider_reduce_into_iter(x: Simd<u8, N>) -> u16 {
-    // FIXME MIR inlining messes up LLVM optimizations.
-    // WOULD-CHECK: zext <8 x i8>
-    // WOULD-CHECK-SAME: to <8 x i16>
-    // WOULD-CHECK: call i16 @llvm.vector.reduce.add.v8i16(<8 x i16>
+    // CHECK: zext <8 x i8>
+    // CHECK-SAME: to <8 x i16>
+    // CHECK: call i16 @llvm.vector.reduce.add.v8i16(<8 x i16>
     x.to_array().into_iter().map(u16::from).sum()
 }
diff --git a/tests/ui/traits/issue-24010.rs b/tests/ui/traits/issue-24010.rs
index f1818533487f6..fd7d6751d5c7b 100644
--- a/tests/ui/traits/issue-24010.rs
+++ b/tests/ui/traits/issue-24010.rs
@@ -1,4 +1,6 @@
 // run-pass
+// revisions: classic next
+//[next] compile-flags: -Ztrait-solver=next
 
 trait Foo: Fn(i32) -> i32 + Send {}
 
diff --git a/tests/ui/traits/new-solver/unsize-although-ambiguous.rs b/tests/ui/traits/new-solver/unsize-although-ambiguous.rs
new file mode 100644
index 0000000000000..431988a5fffa9
--- /dev/null
+++ b/tests/ui/traits/new-solver/unsize-although-ambiguous.rs
@@ -0,0 +1,13 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+
+use std::fmt::Display;
+
+fn box_dyn_display(_: Box<dyn Display>) {}
+
+fn main() {
+    // During coercion, we don't necessarily know whether `{integer}` implements
+    // `Display`. Before, that would cause us to bail out in the coercion loop when
+    // checking `{integer}: Unsize<dyn Display>`.
+    box_dyn_display(Box::new(1));
+}
diff --git a/tests/ui/issues/issue-11515.stderr b/tests/ui/traits/trait-upcasting/issue-11515.current.stderr
similarity index 94%
rename from tests/ui/issues/issue-11515.stderr
rename to tests/ui/traits/trait-upcasting/issue-11515.current.stderr
index accd47f0f5fbc..97d66cccb25be 100644
--- a/tests/ui/issues/issue-11515.stderr
+++ b/tests/ui/traits/trait-upcasting/issue-11515.current.stderr
@@ -1,5 +1,5 @@
 error[E0658]: cannot cast `dyn Fn()` to `dyn FnMut()`, trait upcasting coercion is experimental
-  --> $DIR/issue-11515.rs:9:38
+  --> $DIR/issue-11515.rs:10:38
    |
 LL |     let test = Box::new(Test { func: closure });
    |                                      ^^^^^^^
diff --git a/tests/ui/traits/trait-upcasting/issue-11515.next.stderr b/tests/ui/traits/trait-upcasting/issue-11515.next.stderr
new file mode 100644
index 0000000000000..97d66cccb25be
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/issue-11515.next.stderr
@@ -0,0 +1,13 @@
+error[E0658]: cannot cast `dyn Fn()` to `dyn FnMut()`, trait upcasting coercion is experimental
+  --> $DIR/issue-11515.rs:10:38
+   |
+LL |     let test = Box::new(Test { func: closure });
+   |                                      ^^^^^^^
+   |
+   = note: see issue #65991 <https://github.com/rust-lang/rust/issues/65991> for more information
+   = help: add `#![feature(trait_upcasting)]` to the crate attributes to enable
+   = note: required when coercing `Box<(dyn Fn() + 'static)>` into `Box<(dyn FnMut() + 'static)>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/issues/issue-11515.rs b/tests/ui/traits/trait-upcasting/issue-11515.rs
similarity index 77%
rename from tests/ui/issues/issue-11515.rs
rename to tests/ui/traits/trait-upcasting/issue-11515.rs
index b5c942f96a702..723f3a24fd4e2 100644
--- a/tests/ui/issues/issue-11515.rs
+++ b/tests/ui/traits/trait-upcasting/issue-11515.rs
@@ -1,9 +1,10 @@
+// revisions: current next
+//[next] compile-flags: -Ztrait-solver=next
+
 struct Test {
     func: Box<dyn FnMut() + 'static>,
 }
 
-
-
 fn main() {
     let closure: Box<dyn Fn() + 'static> = Box::new(|| ());
     let test = Box::new(Test { func: closure }); //~ ERROR trait upcasting coercion is experimental [E0658]