From 684e7a5461eda4dee03b9a12d908695ba2f9f27d Mon Sep 17 00:00:00 2001
From: Krasimir Georgiev <krasimir@google.com>
Date: Mon, 27 Mar 2023 11:12:47 +0000
Subject: [PATCH 01/10] llvm-wrapper: adapt for LLVM API change

Adapts the wrapper for the LLVM commit https://github.com/llvm/llvm-project/commit/377e1311d50c7e5b5aab3db081938e0d0ceebdfc.
---
 .../rustc_llvm/llvm-wrapper/PassWrapper.cpp     | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index e604e44a7157b..f2dec8fe3273e 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -1158,13 +1158,6 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
   // Otherwise, we sometimes lose `static` values -- see #60184.
   computeDeadSymbolsWithConstProp(Ret->Index, Ret->GUIDPreservedSymbols,
                                   deadIsPrevailing, /* ImportEnabled = */ false);
-  ComputeCrossModuleImport(
-    Ret->Index,
-    Ret->ModuleToDefinedGVSummaries,
-    Ret->ImportLists,
-    Ret->ExportLists
-  );
-
   // Resolve LinkOnce/Weak symbols, this has to be computed early be cause it
   // impacts the caching.
   //
@@ -1181,6 +1174,16 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
       return true;
     return Prevailing->second == S;
   };
+  ComputeCrossModuleImport(
+    Ret->Index,
+    Ret->ModuleToDefinedGVSummaries,
+#if LLVM_VERSION_GE(17, 0)
+    isPrevailing,
+#endif
+    Ret->ImportLists,
+    Ret->ExportLists
+  );
+
   auto recordNewLinkage = [&](StringRef ModuleIdentifier,
                               GlobalValue::GUID GUID,
                               GlobalValue::LinkageTypes NewLinkage) {

From 1b3fda4978b691b2601ed862efae2798a82ef957 Mon Sep 17 00:00:00 2001
From: Mara Bos <m-ou.se@m-ou.se>
Date: Mon, 27 Mar 2023 14:42:56 +0200
Subject: [PATCH 02/10] Use span of placeholders in format_args!() expansion.

---
 compiler/rustc_ast_lowering/src/format.rs | 66 +++++++++++++++--------
 1 file changed, 43 insertions(+), 23 deletions(-)

diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs
index 72352b138cbf4..c41bdc440935c 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -2,7 +2,7 @@ use super::LoweringContext;
 use rustc_ast as ast;
 use rustc_ast::visit::{self, Visitor};
 use rustc_ast::*;
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_hir as hir;
 use rustc_span::{
     sym,
@@ -238,7 +238,7 @@ fn make_count<'hir>(
     ctx: &mut LoweringContext<'_, 'hir>,
     sp: Span,
     count: &Option<FormatCount>,
-    argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+    argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>,
 ) -> hir::Expr<'hir> {
     match count {
         Some(FormatCount::Literal(n)) => {
@@ -252,7 +252,7 @@ fn make_count<'hir>(
         }
         Some(FormatCount::Argument(arg)) => {
             if let Ok(arg_index) = arg.index {
-                let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
+                let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize), arg.span);
                 let count_param = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
                     sp,
                     hir::LangItem::FormatCount,
@@ -291,12 +291,14 @@ fn make_format_spec<'hir>(
     ctx: &mut LoweringContext<'_, 'hir>,
     sp: Span,
     placeholder: &FormatPlaceholder,
-    argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+    argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>,
 ) -> hir::Expr<'hir> {
     let position = match placeholder.argument.index {
         Ok(arg_index) => {
-            let (i, _) =
-                argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
+            let (i, _) = argmap.insert_full(
+                (arg_index, ArgumentType::Format(placeholder.format_trait)),
+                placeholder.span,
+            );
             ctx.expr_usize(sp, i)
         }
         Err(_) => ctx.expr(
@@ -386,7 +388,7 @@ fn expand_format_args<'hir>(
 
     // Create a list of all _unique_ (argument, format trait) combinations.
     // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
-    let mut argmap = FxIndexSet::default();
+    let mut argmap = FxIndexMap::default();
     for piece in &fmt.template {
         let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
         if placeholder.format_options != Default::default() {
@@ -394,7 +396,10 @@ fn expand_format_args<'hir>(
             use_format_options = true;
         }
         if let Ok(index) = placeholder.argument.index {
-            if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) {
+            if argmap
+                .insert((index, ArgumentType::Format(placeholder.format_trait)), placeholder.span)
+                .is_some()
+            {
                 // Duplicate (argument, format trait) combination,
                 // which we'll only put once in the args array.
                 use_format_options = true;
@@ -438,7 +443,7 @@ fn expand_format_args<'hir>(
     // This is an optimization, speeding up compilation about 1-2% in some cases.
     // See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
     let use_simple_array = argmap.len() == arguments.len()
-        && argmap.iter().enumerate().all(|(i, &(j, _))| i == j)
+        && argmap.iter().enumerate().all(|(i, (&(j, _), _))| i == j)
         && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
 
     let args = if use_simple_array {
@@ -452,14 +457,19 @@ fn expand_format_args<'hir>(
         let elements: Vec<_> = arguments
             .iter()
             .zip(argmap)
-            .map(|(arg, (_, ty))| {
-                let sp = arg.expr.span.with_ctxt(macsp.ctxt());
+            .map(|(arg, ((_, ty), placeholder_span))| {
+                let placeholder_span =
+                    placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
+                let arg_span = match arg.kind {
+                    FormatArgumentKind::Captured(_) => placeholder_span,
+                    _ => arg.expr.span.with_ctxt(macsp.ctxt()),
+                };
                 let arg = ctx.lower_expr(&arg.expr);
                 let ref_arg = ctx.arena.alloc(ctx.expr(
-                    sp,
+                    arg_span,
                     hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
                 ));
-                make_argument(ctx, sp, ref_arg, ty)
+                make_argument(ctx, placeholder_span, ref_arg, ty)
             })
             .collect();
         ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
@@ -475,16 +485,26 @@ fn expand_format_args<'hir>(
         //     }
         let args_ident = Ident::new(sym::args, macsp);
         let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
-        let args = ctx.arena.alloc_from_iter(argmap.iter().map(|&(arg_index, ty)| {
-            let arg = &arguments[arg_index];
-            let sp = arg.expr.span.with_ctxt(macsp.ctxt());
-            let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
-            let arg = ctx.arena.alloc(ctx.expr(
-                sp,
-                hir::ExprKind::Field(args_ident_expr, Ident::new(sym::integer(arg_index), macsp)),
-            ));
-            make_argument(ctx, sp, arg, ty)
-        }));
+        let args = ctx.arena.alloc_from_iter(argmap.iter().map(
+            |(&(arg_index, ty), &placeholder_span)| {
+                let arg = &arguments[arg_index];
+                let placeholder_span =
+                    placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
+                let arg_span = match arg.kind {
+                    FormatArgumentKind::Captured(_) => placeholder_span,
+                    _ => arg.expr.span.with_ctxt(macsp.ctxt()),
+                };
+                let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
+                let arg = ctx.arena.alloc(ctx.expr(
+                    arg_span,
+                    hir::ExprKind::Field(
+                        args_ident_expr,
+                        Ident::new(sym::integer(arg_index), macsp),
+                    ),
+                ));
+                make_argument(ctx, placeholder_span, arg, ty)
+            },
+        ));
         let elements: Vec<_> = arguments
             .iter()
             .map(|arg| {

From 7f395f1effa518708b6b6e30429ba799cf4ba6f6 Mon Sep 17 00:00:00 2001
From: Mara Bos <m-ou.se@m-ou.se>
Date: Mon, 27 Mar 2023 14:43:17 +0200
Subject: [PATCH 03/10] Bless UI tests.

---
 tests/ui/consts/const-eval/format.stderr |  8 ++++----
 tests/ui/fmt/ifmt-bad-arg.stderr         | 14 ++++++--------
 tests/ui/fmt/ifmt-unimpl.stderr          |  4 +++-
 tests/ui/suggestions/issue-97760.stderr  |  4 ++--
 4 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/tests/ui/consts/const-eval/format.stderr b/tests/ui/consts/const-eval/format.stderr
index c39920d444def..70a1abb0a9555 100644
--- a/tests/ui/consts/const-eval/format.stderr
+++ b/tests/ui/consts/const-eval/format.stderr
@@ -1,8 +1,8 @@
 error[E0015]: cannot call non-const formatting macro in constant functions
-  --> $DIR/format.rs:2:20
+  --> $DIR/format.rs:2:13
    |
 LL |     panic!("{:?}", 0);
-   |                    ^
+   |             ^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -17,10 +17,10 @@ LL |     panic!("{:?}", 0);
    = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const formatting macro in constant functions
-  --> $DIR/format.rs:8:22
+  --> $DIR/format.rs:8:15
    |
 LL |     println!("{:?}", 0);
-   |                      ^
+   |               ^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/fmt/ifmt-bad-arg.stderr b/tests/ui/fmt/ifmt-bad-arg.stderr
index d716bbe51affc..bf18fb315c94d 100644
--- a/tests/ui/fmt/ifmt-bad-arg.stderr
+++ b/tests/ui/fmt/ifmt-bad-arg.stderr
@@ -300,10 +300,9 @@ error[E0308]: mismatched types
   --> $DIR/ifmt-bad-arg.rs:78:32
    |
 LL |     println!("{} {:.*} {}", 1, 3.2, 4);
-   |                                ^^^
-   |                                |
-   |                                expected `&usize`, found `&{float}`
-   |                                arguments to this function are incorrect
+   |                    --          ^^^ expected `&usize`, found `&{float}`
+   |                    |
+   |                    arguments to this function are incorrect
    |
    = note: expected reference `&usize`
               found reference `&{float}`
@@ -315,10 +314,9 @@ error[E0308]: mismatched types
   --> $DIR/ifmt-bad-arg.rs:81:35
    |
 LL |     println!("{} {:07$.*} {}", 1, 3.2, 4);
-   |                                   ^^^
-   |                                   |
-   |                                   expected `&usize`, found `&{float}`
-   |                                   arguments to this function are incorrect
+   |                       --          ^^^ expected `&usize`, found `&{float}`
+   |                       |
+   |                       arguments to this function are incorrect
    |
    = note: expected reference `&usize`
               found reference `&{float}`
diff --git a/tests/ui/fmt/ifmt-unimpl.stderr b/tests/ui/fmt/ifmt-unimpl.stderr
index 3480a2ec81548..dc2dee3f3415c 100644
--- a/tests/ui/fmt/ifmt-unimpl.stderr
+++ b/tests/ui/fmt/ifmt-unimpl.stderr
@@ -2,7 +2,9 @@ error[E0277]: the trait bound `str: UpperHex` is not satisfied
   --> $DIR/ifmt-unimpl.rs:2:21
    |
 LL |     format!("{:X}", "3");
-   |                     ^^^ the trait `UpperHex` is not implemented for `str`
+   |              ----   ^^^ the trait `UpperHex` is not implemented for `str`
+   |              |
+   |              required by a bound introduced by this call
    |
    = help: the following other types implement trait `UpperHex`:
              &T
diff --git a/tests/ui/suggestions/issue-97760.stderr b/tests/ui/suggestions/issue-97760.stderr
index bbcc3693fff5a..5415c247c8fff 100644
--- a/tests/ui/suggestions/issue-97760.stderr
+++ b/tests/ui/suggestions/issue-97760.stderr
@@ -1,8 +1,8 @@
 error[E0277]: `<impl IntoIterator as IntoIterator>::Item` doesn't implement `std::fmt::Display`
-  --> $DIR/issue-97760.rs:4:20
+  --> $DIR/issue-97760.rs:4:19
    |
 LL |         println!("{x}");
-   |                    ^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
+   |                   ^^^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
    |
    = help: the trait `std::fmt::Display` is not implemented for `<impl IntoIterator as IntoIterator>::Item`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead

From 769886cc35ce08b76839f4cf72b8af1161c432e1 Mon Sep 17 00:00:00 2001
From: Mara Bos <m-ou.se@m-ou.se>
Date: Mon, 27 Mar 2023 14:56:15 +0200
Subject: [PATCH 04/10] Bless mir-opt tests.

(Only the lifetime spans changed.)
---
 ...mes.foo.ScalarReplacementOfAggregates.diff | 44 +++++++++----------
 1 file changed, 22 insertions(+), 22 deletions(-)

diff --git a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
index 225f80ed41b47..f6f2344e82fac 100644
--- a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
@@ -19,12 +19,12 @@
       let mut _17: &[core::fmt::ArgumentV1<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL
       let _18: &[core::fmt::ArgumentV1<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL
       let _19: [core::fmt::ArgumentV1<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL
-      let mut _20: core::fmt::ArgumentV1<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:21: +10:22
-      let mut _21: &std::boxed::Box<dyn std::fmt::Display>; // in scope 0 at $DIR/lifetimes.rs:+10:21: +10:22
-      let _22: &std::boxed::Box<dyn std::fmt::Display>; // in scope 0 at $DIR/lifetimes.rs:+10:21: +10:22
-      let mut _23: core::fmt::ArgumentV1<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:25: +10:26
-      let mut _24: &u32;                   // in scope 0 at $DIR/lifetimes.rs:+10:25: +10:26
-      let _25: &u32;                       // in scope 0 at $DIR/lifetimes.rs:+10:25: +10:26
+      let mut _20: core::fmt::ArgumentV1<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:20: +10:23
+      let mut _21: &std::boxed::Box<dyn std::fmt::Display>; // in scope 0 at $DIR/lifetimes.rs:+10:20: +10:23
+      let _22: &std::boxed::Box<dyn std::fmt::Display>; // in scope 0 at $DIR/lifetimes.rs:+10:20: +10:23
+      let mut _23: core::fmt::ArgumentV1<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:24: +10:27
+      let mut _24: &u32;                   // in scope 0 at $DIR/lifetimes.rs:+10:24: +10:27
+      let _25: &u32;                       // in scope 0 at $DIR/lifetimes.rs:+10:24: +10:27
       let mut _27: bool;                   // in scope 0 at $DIR/lifetimes.rs:+12:1: +12:2
       let mut _28: isize;                  // in scope 0 at $DIR/lifetimes.rs:+12:1: +12:2
       let mut _29: isize;                  // in scope 0 at $DIR/lifetimes.rs:+12:1: +12:2
@@ -108,34 +108,34 @@
           StorageLive(_17);                // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
           StorageLive(_18);                // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
           StorageLive(_19);                // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
-          StorageLive(_20);                // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
-          StorageLive(_21);                // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
-          StorageLive(_22);                // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
-          _22 = &_8;                       // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
-          _21 = &(*_22);                   // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
-          _20 = core::fmt::ArgumentV1::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _21) -> bb3; // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
+          StorageLive(_20);                // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
+          StorageLive(_21);                // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
+          StorageLive(_22);                // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
+          _22 = &_8;                       // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
+          _21 = &(*_22);                   // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
+          _20 = core::fmt::ArgumentV1::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _21) -> bb3; // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
                                            // mir::Constant
-                                           // + span: $DIR/lifetimes.rs:27:21: 27:22
+                                           // + span: $DIR/lifetimes.rs:27:20: 27:23
                                            // + user_ty: UserType(4)
                                            // + literal: Const { ty: for<'b> fn(&'b Box<dyn std::fmt::Display>) -> core::fmt::ArgumentV1<'b> {core::fmt::ArgumentV1::<'_>::new_display::<Box<dyn std::fmt::Display>>}, val: Value(<ZST>) }
       }
   
       bb3: {
-          StorageDead(_21);                // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
-          StorageLive(_23);                // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
-          StorageLive(_24);                // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
-          StorageLive(_25);                // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
-          _25 = &_6;                       // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
-          _24 = &(*_25);                   // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
-          _23 = core::fmt::ArgumentV1::<'_>::new_display::<u32>(move _24) -> bb4; // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
+          StorageDead(_21);                // scope 4 at $DIR/lifetimes.rs:+10:22: +10:23
+          StorageLive(_23);                // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
+          StorageLive(_24);                // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
+          StorageLive(_25);                // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
+          _25 = &_6;                       // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
+          _24 = &(*_25);                   // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
+          _23 = core::fmt::ArgumentV1::<'_>::new_display::<u32>(move _24) -> bb4; // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
                                            // mir::Constant
-                                           // + span: $DIR/lifetimes.rs:27:25: 27:26
+                                           // + span: $DIR/lifetimes.rs:27:24: 27:27
                                            // + user_ty: UserType(5)
                                            // + literal: Const { ty: for<'b> fn(&'b u32) -> core::fmt::ArgumentV1<'b> {core::fmt::ArgumentV1::<'_>::new_display::<u32>}, val: Value(<ZST>) }
       }
   
       bb4: {
-          StorageDead(_24);                // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
+          StorageDead(_24);                // scope 4 at $DIR/lifetimes.rs:+10:26: +10:27
           _19 = [move _20, move _23];      // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
           StorageDead(_23);                // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
           StorageDead(_20);                // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL

From 6c72a002a68043f9bc67399c43a66e8ab68ca20b Mon Sep 17 00:00:00 2001
From: Mara Bos <m-ou.se@m-ou.se>
Date: Mon, 27 Mar 2023 14:43:25 +0200
Subject: [PATCH 05/10] Add test for span of implicit format args captures.

---
 tests/ui/fmt/format-args-argument-span.rs     | 22 ++++++++
 tests/ui/fmt/format-args-argument-span.stderr | 51 +++++++++++++++++++
 2 files changed, 73 insertions(+)
 create mode 100644 tests/ui/fmt/format-args-argument-span.rs
 create mode 100644 tests/ui/fmt/format-args-argument-span.stderr

diff --git a/tests/ui/fmt/format-args-argument-span.rs b/tests/ui/fmt/format-args-argument-span.rs
new file mode 100644
index 0000000000000..c7acb08f84b6c
--- /dev/null
+++ b/tests/ui/fmt/format-args-argument-span.rs
@@ -0,0 +1,22 @@
+// check-compile
+
+struct DisplayOnly;
+
+impl std::fmt::Display for DisplayOnly {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        unimplemented!()
+    }
+}
+
+fn main() {
+    let x = Some(1);
+    println!("{x:?} {x} {x:?}");
+    //~^ ERROR: `Option<{integer}>` doesn't implement `std::fmt::Display`
+    println!("{x:?} {x} {x:?}", x = Some(1));
+    //~^ ERROR: `Option<{integer}>` doesn't implement `std::fmt::Display`
+    let x = DisplayOnly;
+    println!("{x} {x:?} {x}");
+    //~^ ERROR: `DisplayOnly` doesn't implement `Debug`
+    println!("{x} {x:?} {x}", x = DisplayOnly);
+    //~^ ERROR: `DisplayOnly` doesn't implement `Debug`
+}
diff --git a/tests/ui/fmt/format-args-argument-span.stderr b/tests/ui/fmt/format-args-argument-span.stderr
new file mode 100644
index 0000000000000..b060b2cd33930
--- /dev/null
+++ b/tests/ui/fmt/format-args-argument-span.stderr
@@ -0,0 +1,51 @@
+error[E0277]: `Option<{integer}>` doesn't implement `std::fmt::Display`
+  --> $DIR/format-args-argument-span.rs:13:21
+   |
+LL |     println!("{x:?} {x} {x:?}");
+   |                     ^^^ `Option<{integer}>` cannot be formatted with the default formatter
+   |
+   = help: the trait `std::fmt::Display` is not implemented for `Option<{integer}>`
+   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0277]: `Option<{integer}>` doesn't implement `std::fmt::Display`
+  --> $DIR/format-args-argument-span.rs:15:37
+   |
+LL |     println!("{x:?} {x} {x:?}", x = Some(1));
+   |                                     ^^^^^^^ `Option<{integer}>` cannot be formatted with the default formatter
+   |
+   = help: the trait `std::fmt::Display` is not implemented for `Option<{integer}>`
+   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0277]: `DisplayOnly` doesn't implement `Debug`
+  --> $DIR/format-args-argument-span.rs:18:19
+   |
+LL |     println!("{x} {x:?} {x}");
+   |                   ^^^^^ `DisplayOnly` cannot be formatted using `{:?}`
+   |
+   = help: the trait `Debug` is not implemented for `DisplayOnly`
+   = note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly`
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider annotating `DisplayOnly` with `#[derive(Debug)]`
+   |
+LL | #[derive(Debug)]
+   |
+
+error[E0277]: `DisplayOnly` doesn't implement `Debug`
+  --> $DIR/format-args-argument-span.rs:20:35
+   |
+LL |     println!("{x} {x:?} {x}", x = DisplayOnly);
+   |                                   ^^^^^^^^^^^ `DisplayOnly` cannot be formatted using `{:?}`
+   |
+   = help: the trait `Debug` is not implemented for `DisplayOnly`
+   = note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly`
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider annotating `DisplayOnly` with `#[derive(Debug)]`
+   |
+LL | #[derive(Debug)]
+   |
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.

From 52b15b4bf9f3a24b44017fe4b1c114a6d91f1e88 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Date: Thu, 23 Mar 2023 18:47:49 +0400
Subject: [PATCH 06/10] rustdoc: Unsupport importing `doc(primitive)` and
 `doc(keyword)` modules

These are internal features used for a specific purpose, and modules without imports are enough for that purpose.
---
 src/librustdoc/clean/types.rs  | 17 -----------------
 tests/rustdoc/issue-15318-2.rs |  2 +-
 2 files changed, 1 insertion(+), 18 deletions(-)

diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 9019a6c49ecc5..93c2b5e069c4c 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -232,14 +232,6 @@ impl ExternalCrate {
                         hir::ItemKind::Mod(_) => {
                             as_keyword(Res::Def(DefKind::Mod, id.owner_id.to_def_id()))
                         }
-                        hir::ItemKind::Use(path, hir::UseKind::Single)
-                            if tcx.visibility(id.owner_id).is_public() =>
-                        {
-                            path.res
-                                .iter()
-                                .find_map(|res| as_keyword(res.expect_non_local()))
-                                .map(|(_, prim)| (id.owner_id.to_def_id(), prim))
-                        }
                         _ => None,
                     }
                 })
@@ -302,15 +294,6 @@ impl ExternalCrate {
                         hir::ItemKind::Mod(_) => {
                             as_primitive(Res::Def(DefKind::Mod, id.owner_id.to_def_id()))
                         }
-                        hir::ItemKind::Use(path, hir::UseKind::Single)
-                            if tcx.visibility(id.owner_id).is_public() =>
-                        {
-                            path.res
-                                .iter()
-                                .find_map(|res| as_primitive(res.expect_non_local()))
-                                // Pretend the primitive is local.
-                                .map(|(_, prim)| (id.owner_id.to_def_id(), prim))
-                        }
                         _ => None,
                     }
                 })
diff --git a/tests/rustdoc/issue-15318-2.rs b/tests/rustdoc/issue-15318-2.rs
index f7f5052a36dd3..614f2c1c08e87 100644
--- a/tests/rustdoc/issue-15318-2.rs
+++ b/tests/rustdoc/issue-15318-2.rs
@@ -6,7 +6,7 @@ extern crate issue_15318;
 
 pub use issue_15318::ptr;
 
-// @has issue_15318_2/fn.bar.html \
+// @!has issue_15318_2/fn.bar.html \
 //          '//*[@href="primitive.pointer.html"]' \
 //          '*mut T'
 pub fn bar<T>(ptr: *mut T) {}

From be9fd75d32f9af0ebed5aad348891603f2e17ac2 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Wed, 22 Feb 2023 04:43:15 +0000
Subject: [PATCH 07/10] rustdoc + rustdoc-json support for non_lifetime_binders

---
 src/librustdoc/clean/mod.rs                | 17 ++---------
 src/librustdoc/clean/simplify.rs           |  6 +---
 src/librustdoc/clean/types.rs              |  8 ++---
 src/librustdoc/html/format.rs              |  4 +--
 src/librustdoc/json/conversions.rs         | 34 +++++++++++++++++++---
 tests/rustdoc-json/non_lifetime_binders.rs | 24 +++++++++++++++
 tests/rustdoc/non_lifetime_binders.rs      |  9 ++++++
 7 files changed, 73 insertions(+), 29 deletions(-)
 create mode 100644 tests/rustdoc-json/non_lifetime_binders.rs
 create mode 100644 tests/rustdoc/non_lifetime_binders.rs

diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 5f5521caf68b9..2d247bd537bdf 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -31,7 +31,6 @@ use rustc_span::hygiene::{AstPass, MacroKind};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{self, ExpnKind};
 
-use std::assert_matches::assert_matches;
 use std::borrow::Cow;
 use std::collections::hash_map::Entry;
 use std::collections::BTreeMap;
@@ -270,15 +269,7 @@ fn clean_where_predicate<'tcx>(
             let bound_params = wbp
                 .bound_generic_params
                 .iter()
-                .map(|param| {
-                    // Higher-ranked params must be lifetimes.
-                    // Higher-ranked lifetimes can't have bounds.
-                    assert_matches!(
-                        param,
-                        hir::GenericParam { kind: hir::GenericParamKind::Lifetime { .. }, .. }
-                    );
-                    Lifetime(param.name.ident().name)
-                })
+                .map(|param| clean_generic_param(cx, None, param))
                 .collect();
             WherePredicate::BoundPredicate {
                 ty: clean_ty(wbp.bounded_ty, cx),
@@ -410,7 +401,7 @@ fn clean_projection_predicate<'tcx>(
         .collect_referenced_late_bound_regions(&pred)
         .into_iter()
         .filter_map(|br| match br {
-            ty::BrNamed(_, name) if br.is_named() => Some(Lifetime(name)),
+            ty::BrNamed(_, name) if br.is_named() => Some(GenericParamDef::lifetime(name)),
             _ => None,
         })
         .collect();
@@ -508,7 +499,6 @@ fn clean_generic_param_def<'tcx>(
         ty::GenericParamDefKind::Const { has_default } => (
             def.name,
             GenericParamDefKind::Const {
-                did: def.def_id,
                 ty: Box::new(clean_middle_ty(
                     ty::Binder::dummy(
                         cx.tcx
@@ -578,7 +568,6 @@ fn clean_generic_param<'tcx>(
         hir::GenericParamKind::Const { ty, default } => (
             param.name.ident().name,
             GenericParamDefKind::Const {
-                did: param.def_id.to_def_id(),
                 ty: Box::new(clean_ty(ty, cx)),
                 default: default
                     .map(|ct| Box::new(ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string())),
@@ -831,7 +820,7 @@ fn clean_ty_generics<'tcx>(
                         p.get_bound_params()
                             .into_iter()
                             .flatten()
-                            .map(|param| GenericParamDef::lifetime(param.0))
+                            .cloned()
                             .collect(),
                     ));
                 }
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index dbbc25739aa07..3c72b0bf9f2f6 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -49,11 +49,7 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> ThinVec<WP
     equalities.retain(|(lhs, rhs, bound_params)| {
         let Some((ty, trait_did, name)) = lhs.projection() else { return true; };
         let Some((bounds, _)) = tybounds.get_mut(ty) else { return true };
-        let bound_params = bound_params
-            .into_iter()
-            .map(|param| clean::GenericParamDef::lifetime(param.0))
-            .collect();
-        merge_bounds(cx, bounds, bound_params, trait_did, name, rhs)
+        merge_bounds(cx, bounds, bound_params.clone(), trait_did, name, rhs)
     });
 
     // And finally, let's reassemble everything
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 9019a6c49ecc5..7c68f60426b07 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1236,9 +1236,9 @@ impl Lifetime {
 
 #[derive(Clone, Debug)]
 pub(crate) enum WherePredicate {
-    BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<Lifetime> },
+    BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<GenericParamDef> },
     RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> },
-    EqPredicate { lhs: Box<Type>, rhs: Box<Term>, bound_params: Vec<Lifetime> },
+    EqPredicate { lhs: Box<Type>, rhs: Box<Term>, bound_params: Vec<GenericParamDef> },
 }
 
 impl WherePredicate {
@@ -1250,7 +1250,7 @@ impl WherePredicate {
         }
     }
 
-    pub(crate) fn get_bound_params(&self) -> Option<&[Lifetime]> {
+    pub(crate) fn get_bound_params(&self) -> Option<&[GenericParamDef]> {
         match self {
             Self::BoundPredicate { bound_params, .. } | Self::EqPredicate { bound_params, .. } => {
                 Some(bound_params)
@@ -1264,7 +1264,7 @@ impl WherePredicate {
 pub(crate) enum GenericParamDefKind {
     Lifetime { outlives: Vec<Lifetime> },
     Type { did: DefId, bounds: Vec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
-    Const { did: DefId, ty: Box<Type>, default: Option<Box<String>> },
+    Const { ty: Box<Type>, default: Option<Box<String>> },
 }
 
 impl GenericParamDefKind {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index f2b9c0bcf3ee7..0895bb510d481 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -307,13 +307,13 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
                                 write!(
                                     f,
                                     "for<{:#}> {ty_cx:#}: {generic_bounds:#}",
-                                    comma_sep(bound_params.iter().map(|lt| lt.print()), true)
+                                    comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true)
                                 )
                             } else {
                                 write!(
                                     f,
                                     "for&lt;{}&gt; {ty_cx}: {generic_bounds}",
-                                    comma_sep(bound_params.iter().map(|lt| lt.print()), true)
+                                    comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true)
                                 )
                             }
                         }
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 18c45fd699155..59d67f27b3006 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -456,7 +456,7 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
                 default: default.map(|x| (*x).into_tcx(tcx)),
                 synthetic,
             },
-            Const { did: _, ty, default } => GenericParamDefKind::Const {
+            Const { ty, default } => GenericParamDefKind::Const {
                 type_: (*ty).into_tcx(tcx),
                 default: default.map(|x| *x),
             },
@@ -473,9 +473,35 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
                 bounds: bounds.into_tcx(tcx),
                 generic_params: bound_params
                     .into_iter()
-                    .map(|x| GenericParamDef {
-                        name: x.0.to_string(),
-                        kind: GenericParamDefKind::Lifetime { outlives: vec![] },
+                    .map(|x| {
+                        let name = x.name.to_string();
+                        let kind = match x.kind {
+                            clean::GenericParamDefKind::Lifetime { outlives } => {
+                                GenericParamDefKind::Lifetime {
+                                    outlives: outlives.iter().map(|lt| lt.0.to_string()).collect(),
+                                }
+                            }
+                            clean::GenericParamDefKind::Type {
+                                did: _,
+                                bounds,
+                                default,
+                                synthetic,
+                            } => GenericParamDefKind::Type {
+                                bounds: bounds
+                                    .into_iter()
+                                    .map(|bound| bound.into_tcx(tcx))
+                                    .collect(),
+                                default: default.map(|ty| (*ty).into_tcx(tcx)),
+                                synthetic,
+                            },
+                            clean::GenericParamDefKind::Const { ty, default } => {
+                                GenericParamDefKind::Const {
+                                    type_: (*ty).into_tcx(tcx),
+                                    default: default.map(|d| *d),
+                                }
+                            }
+                        };
+                        GenericParamDef { name, kind }
                     })
                     .collect(),
             },
diff --git a/tests/rustdoc-json/non_lifetime_binders.rs b/tests/rustdoc-json/non_lifetime_binders.rs
new file mode 100644
index 0000000000000..ca5a008344af2
--- /dev/null
+++ b/tests/rustdoc-json/non_lifetime_binders.rs
@@ -0,0 +1,24 @@
+// ignore-tidy-linelength
+
+#![feature(non_lifetime_binders)]
+#![allow(incomplete_features)]
+
+#![no_core]
+#![feature(lang_items, no_core)]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+pub trait Trait {}
+
+#[lang = "phantom_data"]
+struct PhantomData<T_>;
+
+pub struct Wrapper<T_>(PhantomData<T_>);
+
+// @count "$.index[*][?(@.name=='foo')].inner.generics.where_predicates[0].bound_predicate.generic_params[*]" 2
+// @is "$.index[*][?(@.name=='foo')].inner.generics.where_predicates[0].bound_predicate.generic_params[0].name" \"\'a\"
+// @is "$.index[*][?(@.name=='foo')].inner.generics.where_predicates[0].bound_predicate.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }'
+// @is "$.index[*][?(@.name=='foo')].inner.generics.where_predicates[0].bound_predicate.generic_params[1].name" \"T\"
+// @is "$.index[*][?(@.name=='foo')].inner.generics.where_predicates[0].bound_predicate.generic_params[1].kind" '{ "type": { "bounds": [], "default": null, "synthetic": false } }'
+pub fn foo() where for<'a, T> &'a Wrapper<T>: Trait {}
diff --git a/tests/rustdoc/non_lifetime_binders.rs b/tests/rustdoc/non_lifetime_binders.rs
new file mode 100644
index 0000000000000..da9a4e6a84d5a
--- /dev/null
+++ b/tests/rustdoc/non_lifetime_binders.rs
@@ -0,0 +1,9 @@
+#![feature(non_lifetime_binders)]
+#![allow(incomplete_features)]
+
+pub trait Trait {}
+
+pub struct Wrapper<T: ?Sized>(Box<T>);
+
+// @has non_lifetime_binders/fn.foo.html '//pre' "fn foo()where for<'a, T> &'a Wrapper<T>: Trait"
+pub fn foo() where for<'a, T> &'a Wrapper<T>: Trait {}

From ef5f773bffdc2fb28bc833fa6132cbdade1dc549 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Mon, 27 Mar 2023 21:48:43 +0000
Subject: [PATCH 08/10] Check for overflow in
 assemble_candidates_after_normalizing_self_ty

---
 .../src/solve/assembly.rs                     | 50 ++++++++++++-------
 .../src/solve/search_graph/overflow.rs        | 21 ++++++++
 .../recursive-self-normalization-2.rs         | 19 +++++++
 .../recursive-self-normalization-2.stderr     |  9 ++++
 .../recursive-self-normalization.rs           | 15 ++++++
 .../recursive-self-normalization.stderr       |  9 ++++
 6 files changed, 105 insertions(+), 18 deletions(-)
 create mode 100644 tests/ui/traits/new-solver/recursive-self-normalization-2.rs
 create mode 100644 tests/ui/traits/new-solver/recursive-self-normalization-2.stderr
 create mode 100644 tests/ui/traits/new-solver/recursive-self-normalization.rs
 create mode 100644 tests/ui/traits/new-solver/recursive-self-normalization.stderr

diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs
index 4fb77d795189d..0f7a0eb337ba6 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly.rs
@@ -1,5 +1,6 @@
 //! Code shared by trait and projection goals for candidate assembly.
 
+use super::search_graph::OverflowHandler;
 #[cfg(doc)]
 use super::trait_goals::structural_traits::*;
 use super::{EvalCtxt, SolverMode};
@@ -279,25 +280,38 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             return
         };
 
-        self.probe(|ecx| {
-            let normalized_ty = ecx.next_ty_infer();
-            let normalizes_to_goal = goal.with(
-                tcx,
-                ty::Binder::dummy(ty::ProjectionPredicate {
-                    projection_ty,
-                    term: normalized_ty.into(),
-                }),
-            );
-            ecx.add_goal(normalizes_to_goal);
-            if let Ok(_) = ecx.try_evaluate_added_goals() {
-                let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
-
-                // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
-                // This doesn't work as long as we use `CandidateSource` in winnowing.
-                let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
-                candidates.extend(ecx.assemble_and_evaluate_candidates(goal));
-            }
+        let normalized_self_candidates: Result<_, NoSolution> = self.probe(|ecx| {
+            ecx.with_incremented_depth(
+                |ecx| {
+                    let result = ecx.evaluate_added_goals_and_make_canonical_response(
+                        Certainty::Maybe(MaybeCause::Overflow),
+                    )?;
+                    Ok(vec![Candidate { source: CandidateSource::BuiltinImpl, result }])
+                },
+                |ecx| {
+                    let normalized_ty = ecx.next_ty_infer();
+                    let normalizes_to_goal = goal.with(
+                        tcx,
+                        ty::Binder::dummy(ty::ProjectionPredicate {
+                            projection_ty,
+                            term: normalized_ty.into(),
+                        }),
+                    );
+                    ecx.add_goal(normalizes_to_goal);
+                    let _ = ecx.try_evaluate_added_goals()?;
+                    let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
+                    // NOTE: Alternatively we could call `evaluate_goal` here and only
+                    // have a `Normalized` candidate. This doesn't work as long as we
+                    // use `CandidateSource` in winnowing.
+                    let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
+                    Ok(ecx.assemble_and_evaluate_candidates(goal))
+                },
+            )
         });
+
+        if let Ok(normalized_self_candidates) = normalized_self_candidates {
+            candidates.extend(normalized_self_candidates);
+        }
     }
 
     fn assemble_impl_candidates<G: GoalKind<'tcx>>(
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs
index 7c9e63f529b55..574f3e9a5772d 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs
@@ -73,6 +73,27 @@ pub(in crate::solve) trait OverflowHandler<'tcx> {
         self.search_graph().overflow_data.deal_with_overflow();
         on_overflow(self)
     }
+
+    // Increment the `additional_depth` by one and evaluate `body`, or `on_overflow`
+    // if the depth is overflown.
+    fn with_incremented_depth<T>(
+        &mut self,
+        on_overflow: impl FnOnce(&mut Self) -> T,
+        body: impl FnOnce(&mut Self) -> T,
+    ) -> T {
+        let depth = self.search_graph().stack.len();
+        self.search_graph().overflow_data.additional_depth += 1;
+
+        let result = if self.search_graph().overflow_data.has_overflow(depth) {
+            self.search_graph().overflow_data.deal_with_overflow();
+            on_overflow(self)
+        } else {
+            body(self)
+        };
+
+        self.search_graph().overflow_data.additional_depth -= 1;
+        result
+    }
 }
 
 impl<'tcx> OverflowHandler<'tcx> for EvalCtxt<'_, 'tcx> {
diff --git a/tests/ui/traits/new-solver/recursive-self-normalization-2.rs b/tests/ui/traits/new-solver/recursive-self-normalization-2.rs
new file mode 100644
index 0000000000000..7417d6018a131
--- /dev/null
+++ b/tests/ui/traits/new-solver/recursive-self-normalization-2.rs
@@ -0,0 +1,19 @@
+// compile-flags: -Ztrait-solver=next
+
+trait Foo1 {
+    type Assoc1;
+}
+
+trait Foo2 {
+    type Assoc2;
+}
+
+trait Bar {}
+fn needs_bar<S: Bar>() {}
+
+fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() {
+    needs_bar::<T::Assoc1>();
+    //~^ ERROR type annotations needed
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/recursive-self-normalization-2.stderr b/tests/ui/traits/new-solver/recursive-self-normalization-2.stderr
new file mode 100644
index 0000000000000..29cfa47a1050a
--- /dev/null
+++ b/tests/ui/traits/new-solver/recursive-self-normalization-2.stderr
@@ -0,0 +1,9 @@
+error[E0282]: type annotations needed
+  --> $DIR/recursive-self-normalization-2.rs:15:5
+   |
+LL |     needs_bar::<T::Assoc1>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `S` declared on the function `needs_bar`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/traits/new-solver/recursive-self-normalization.rs b/tests/ui/traits/new-solver/recursive-self-normalization.rs
new file mode 100644
index 0000000000000..f3e3d71d813e4
--- /dev/null
+++ b/tests/ui/traits/new-solver/recursive-self-normalization.rs
@@ -0,0 +1,15 @@
+// compile-flags: -Ztrait-solver=next
+
+trait Foo {
+    type Assoc;
+}
+
+trait Bar {}
+fn needs_bar<S: Bar>() {}
+
+fn test<T: Foo<Assoc = <T as Foo>::Assoc>>() {
+    needs_bar::<T::Assoc>();
+    //~^ ERROR type annotations needed
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/recursive-self-normalization.stderr b/tests/ui/traits/new-solver/recursive-self-normalization.stderr
new file mode 100644
index 0000000000000..ba39981893d44
--- /dev/null
+++ b/tests/ui/traits/new-solver/recursive-self-normalization.stderr
@@ -0,0 +1,9 @@
+error[E0282]: type annotations needed
+  --> $DIR/recursive-self-normalization.rs:11:5
+   |
+LL |     needs_bar::<T::Assoc>();
+   |     ^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `S` declared on the function `needs_bar`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.

From c5da0619d102493ce39bc36554e14191e964c1b4 Mon Sep 17 00:00:00 2001
From: David Tolnay <dtolnay@gmail.com>
Date: Tue, 28 Mar 2023 20:21:23 -0700
Subject: [PATCH 09/10] Fix mismatched punctuation in Debug impl of AttrId

---
 compiler/rustc_ast/src/ast.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index ab8b7f632e8ef..cc0fc7b8358fb 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2570,7 +2570,7 @@ pub enum AttrStyle {
 
 rustc_index::newtype_index! {
     #[custom_encodable]
-    #[debug_format = "AttrId({})]"]
+    #[debug_format = "AttrId({})"]
     pub struct AttrId {}
 }
 

From 843c5e361e1b392f89f71ac26a951f8dfd84e43a Mon Sep 17 00:00:00 2001
From: Scott McMurray <scottmcm@users.noreply.github.com>
Date: Wed, 29 Mar 2023 00:04:14 -0700
Subject: [PATCH 10/10] =?UTF-8?q?Rename=20`IndexVec::last`=20=E2=86=92=20`?=
 =?UTF-8?q?last=5Findex`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

As I've been trying to replace a `Vec` with an `IndexVec`, having `last` exist on both but returning very different types makes the transition a bit awkward -- the errors are later, where you get things like "there's no `ty` method on `mir::Field`" rather than a more localized error like "hey, there's no `last` on `IndexVec`".

So I propose renaming `last` to `last_index` to help distinguish `Vec::last`, which returns an element, and `IndexVec::last_index`, which returns an index.

(Similarly, `Iterator::last` also returns an element, not an index.)
---
 compiler/rustc_const_eval/src/transform/promote_consts.rs    | 4 ++--
 compiler/rustc_index/src/vec.rs                              | 2 +-
 compiler/rustc_mir_transform/src/coverage/tests.rs           | 2 +-
 compiler/rustc_trait_selection/src/solve/search_graph/mod.rs | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 648a86d32fcf3..40c848de2beed 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -707,7 +707,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
     }
 
     fn assign(&mut self, dest: Local, rvalue: Rvalue<'tcx>, span: Span) {
-        let last = self.promoted.basic_blocks.last().unwrap();
+        let last = self.promoted.basic_blocks.last_index().unwrap();
         let data = &mut self.promoted[last];
         data.statements.push(Statement {
             source_info: SourceInfo::outermost(span),
@@ -800,7 +800,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                         self.visit_operand(arg, loc);
                     }
 
-                    let last = self.promoted.basic_blocks.last().unwrap();
+                    let last = self.promoted.basic_blocks.last_index().unwrap();
                     let new_target = self.new_block();
 
                     *self.promoted[last].terminator_mut() = Terminator {
diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs
index 68cdc6d7711d4..acf883fe90cec 100644
--- a/compiler/rustc_index/src/vec.rs
+++ b/compiler/rustc_index/src/vec.rs
@@ -216,7 +216,7 @@ impl<I: Idx, T> IndexVec<I, T> {
     }
 
     #[inline]
-    pub fn last(&self) -> Option<I> {
+    pub fn last_index(&self) -> Option<I> {
         self.len().checked_sub(1).map(I::new)
     }
 
diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs
index aded8039dc313..59b506e734553 100644
--- a/compiler/rustc_mir_transform/src/coverage/tests.rs
+++ b/compiler/rustc_mir_transform/src/coverage/tests.rs
@@ -65,7 +65,7 @@ impl<'tcx> MockBlocks<'tcx> {
     }
 
     fn push(&mut self, kind: TerminatorKind<'tcx>) -> BasicBlock {
-        let next_lo = if let Some(last) = self.blocks.last() {
+        let next_lo = if let Some(last) = self.blocks.last_index() {
             self.blocks[last].terminator().source_info.span.hi()
         } else {
             BytePos(1)
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
index 9773a3eacd6fa..717905e55e573 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -54,7 +54,7 @@ impl<'tcx> SearchGraph<'tcx> {
     /// Whether we're currently in a cycle. This should only be used
     /// for debug assertions.
     pub(super) fn in_cycle(&self) -> bool {
-        if let Some(stack_depth) = self.stack.last() {
+        if let Some(stack_depth) = self.stack.last_index() {
             // Either the current goal on the stack is the root of a cycle...
             if self.stack[stack_depth].has_been_used {
                 return true;