diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
index 0990b9bee90f6..523841951b078 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -215,7 +215,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         // created inside of the query directly instead of returning them to the
         // caller.
         let prev_universe = self.infcx.universe();
-        let universes_created_in_query = response.max_universe.index() + 1;
+        let universes_created_in_query = response.max_universe.index();
         for _ in 0..universes_created_in_query {
             self.infcx.create_next_universe();
         }
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index 41aac02eaa97a..515d9d62ad392 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -119,6 +119,7 @@
 #![feature(const_waker)]
 #![feature(core_intrinsics)]
 #![feature(core_panic)]
+#![feature(deprecated_suggestion)]
 #![feature(dispatch_from_dyn)]
 #![feature(error_generic_member_access)]
 #![feature(error_in_core)]
diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs
index 093dcbbe8bf77..aa3b7b7e1914b 100644
--- a/library/alloc/src/slice.rs
+++ b/library/alloc/src/slice.rs
@@ -592,7 +592,7 @@ impl<T> [T] {
     /// ```
     #[rustc_allow_incoherent_impl]
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[deprecated(since = "1.3.0", note = "renamed to join")]
+    #[deprecated(since = "1.3.0", note = "renamed to join", suggestion = "join")]
     pub fn connect<Separator>(&self, sep: Separator) -> <Self as Join<Separator>>::Output
     where
         Self: Join<Separator>,
diff --git a/library/core/src/error.rs b/library/core/src/error.rs
index 1170221c10c43..650e626c88144 100644
--- a/library/core/src/error.rs
+++ b/library/core/src/error.rs
@@ -114,7 +114,8 @@ pub trait Error: Debug + Display {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[deprecated(
         since = "1.33.0",
-        note = "replaced by Error::source, which can support downcasting"
+        note = "replaced by Error::source, which can support downcasting",
+        suggestion = "source"
     )]
     #[allow(missing_docs)]
     fn cause(&self) -> Option<&dyn Error> {
diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index 92e38df404980..163a65c909e45 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -253,7 +253,7 @@ impl CStr {
     /// ```
     ///
     /// [valid]: core::ptr#safety
-    #[inline]
+    #[inline] // inline is necessary for codegen to see strlen.
     #[must_use]
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "113219")]
@@ -280,6 +280,8 @@ impl CStr {
                 len
             }
 
+            // `inline` is necessary for codegen to see strlen.
+            #[inline]
             fn strlen_rt(s: *const c_char) -> usize {
                 extern "C" {
                     /// Provided by libc or compiler_builtins.
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index 2fff3f0efd73a..732fcce0f2930 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -413,7 +413,7 @@ pub const unsafe fn size_of_val_raw<T: ?Sized>(val: *const T) -> usize {
 #[inline]
 #[must_use]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[deprecated(note = "use `align_of` instead", since = "1.2.0")]
+#[deprecated(note = "use `align_of` instead", since = "1.2.0", suggestion = "align_of")]
 pub fn min_align_of<T>() -> usize {
     intrinsics::min_align_of::<T>()
 }
@@ -436,7 +436,7 @@ pub fn min_align_of<T>() -> usize {
 #[inline]
 #[must_use]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[deprecated(note = "use `align_of_val` instead", since = "1.2.0")]
+#[deprecated(note = "use `align_of_val` instead", since = "1.2.0", suggestion = "align_of_val")]
 pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
     // SAFETY: val is a reference, so it's a valid raw pointer
     unsafe { intrinsics::min_align_of_val(val) }
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index e5f34952c7d40..23cebdb6c3b90 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -997,7 +997,7 @@ impl str {
 
     /// An iterator over the lines of a string.
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[deprecated(since = "1.4.0", note = "use lines() instead now")]
+    #[deprecated(since = "1.4.0", note = "use lines() instead now", suggestion = "lines")]
     #[inline]
     #[allow(deprecated)]
     pub fn lines_any(&self) -> LinesAny<'_> {
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index a083b65604d4e..be173a7ace6d8 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -49,12 +49,14 @@ use crate::sys;
 /// ```
 ///
 /// In other words, if two keys are equal, their hashes must be equal.
+/// Violating this property is a logic error.
 ///
-/// It is a logic error for a key to be modified in such a way that the key's
+/// It is also a logic error for a key to be modified in such a way that the key's
 /// hash, as determined by the [`Hash`] trait, or its equality, as determined by
 /// the [`Eq`] trait, changes while it is in the map. This is normally only
 /// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code.
-/// The behavior resulting from such a logic error is not specified, but will
+///
+/// The behavior resulting from either logic error is not specified, but will
 /// be encapsulated to the `HashMap` that observed the logic error and not
 /// result in undefined behavior. This could include panics, incorrect results,
 /// aborts, memory leaks, and non-termination.
diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs
index 959403e164476..6d85b26af5fa2 100644
--- a/library/std/src/collections/hash/set.rs
+++ b/library/std/src/collections/hash/set.rs
@@ -24,13 +24,14 @@ use super::map::{map_try_reserve_error, RandomState};
 /// ```
 ///
 /// In other words, if two keys are equal, their hashes must be equal.
+/// Violating this property is a logic error.
 ///
-///
-/// It is a logic error for a key to be modified in such a way that the key's
+/// It is also a logic error for a key to be modified in such a way that the key's
 /// hash, as determined by the [`Hash`] trait, or its equality, as determined by
 /// the [`Eq`] trait, changes while it is in the map. This is normally only
 /// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code.
-/// The behavior resulting from such a logic error is not specified, but will
+///
+/// The behavior resulting from either logic error is not specified, but will
 /// be encapsulated to the `HashSet` that observed the logic error and not
 /// result in undefined behavior. This could include panics, incorrect results,
 /// aborts, memory leaks, and non-termination.
diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index 2b40b672d9f0a..a1d2a77b2e7ac 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -109,7 +109,11 @@ pub trait CommandExt: Sealed {
     ///
     /// [`pre_exec`]: CommandExt::pre_exec
     #[stable(feature = "process_exec", since = "1.15.0")]
-    #[deprecated(since = "1.37.0", note = "should be unsafe, use `pre_exec` instead")]
+    #[deprecated(
+        since = "1.37.0",
+        note = "should be unsafe, use `pre_exec` instead",
+        suggestion = "pre_exec"
+    )]
     fn before_exec<F>(&mut self, f: F) -> &mut process::Command
     where
         F: FnMut() -> io::Result<()> + Send + Sync + 'static,
diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs
index 9c4b926b7ecdc..97c3b5fb1249e 100644
--- a/library/std/src/sync/condvar.rs
+++ b/library/std/src/sync/condvar.rs
@@ -303,7 +303,11 @@ impl Condvar {
     /// }
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[deprecated(since = "1.6.0", note = "replaced by `std::sync::Condvar::wait_timeout`")]
+    #[deprecated(
+        since = "1.6.0",
+        note = "replaced by `std::sync::Condvar::wait_timeout`",
+        suggestion = "wait_timeout"
+    )]
     pub fn wait_timeout_ms<'a, T>(
         &self,
         guard: MutexGuard<'a, T>,
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index e4581c2de7825..762e2f25c122e 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -825,7 +825,7 @@ pub fn panicking() -> bool {
 /// thread::sleep_ms(2000);
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
-#[deprecated(since = "1.6.0", note = "replaced by `std::thread::sleep`")]
+#[deprecated(since = "1.6.0", note = "replaced by `std::thread::sleep`", suggestion = "sleep")]
 pub fn sleep_ms(ms: u32) {
     sleep(Duration::from_millis(ms as u64))
 }
@@ -1004,7 +1004,11 @@ pub fn park() {
 ///
 /// See the [park documentation][`park`] for more detail.
 #[stable(feature = "rust1", since = "1.0.0")]
-#[deprecated(since = "1.6.0", note = "replaced by `std::thread::park_timeout`")]
+#[deprecated(
+    since = "1.6.0",
+    note = "replaced by `std::thread::park_timeout`",
+    suggestion = "park_timeout"
+)]
 pub fn park_timeout_ms(ms: u32) {
     park_timeout(Duration::from_millis(ms as u64))
 }
diff --git a/tests/ui/deprecation/issue-84637-deprecated-associated-function.fixed b/tests/ui/deprecation/issue-84637-deprecated-associated-function.fixed
index 85e882870946b..659b546552229 100644
--- a/tests/ui/deprecation/issue-84637-deprecated-associated-function.fixed
+++ b/tests/ui/deprecation/issue-84637-deprecated-associated-function.fixed
@@ -6,4 +6,6 @@ fn main() {
     let _foo = str::trim_start("   aoeu"); //~ ERROR use of deprecated method `core::str::<impl str>::trim_left`: superseded by `trim_start` [deprecated]
 
     let _bar = "   aoeu".trim_start(); //~ ERROR use of deprecated method `core::str::<impl str>::trim_left`: superseded by `trim_start` [deprecated]
+
+    let _baz = ["a", "b"].join(" "); //~ ERROR use of deprecated method `std::slice::<impl [T]>::connect`: renamed to join [deprecated]
 }
diff --git a/tests/ui/deprecation/issue-84637-deprecated-associated-function.rs b/tests/ui/deprecation/issue-84637-deprecated-associated-function.rs
index 246de2f5e4bfe..cfc6c4450b4ab 100644
--- a/tests/ui/deprecation/issue-84637-deprecated-associated-function.rs
+++ b/tests/ui/deprecation/issue-84637-deprecated-associated-function.rs
@@ -6,4 +6,6 @@ fn main() {
     let _foo = str::trim_left("   aoeu"); //~ ERROR use of deprecated method `core::str::<impl str>::trim_left`: superseded by `trim_start` [deprecated]
 
     let _bar = "   aoeu".trim_left(); //~ ERROR use of deprecated method `core::str::<impl str>::trim_left`: superseded by `trim_start` [deprecated]
+
+    let _baz = ["a", "b"].connect(" "); //~ ERROR use of deprecated method `std::slice::<impl [T]>::connect`: renamed to join [deprecated]
 }
diff --git a/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr b/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr
index 3b518d1802bc8..d1f5ea3602a8f 100644
--- a/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr
+++ b/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr
@@ -25,5 +25,16 @@ help: replace the use of the deprecated method
 LL |     let _bar = "   aoeu".trim_start();
    |                          ~~~~~~~~~~
 
-error: aborting due to 2 previous errors
+error: use of deprecated method `std::slice::<impl [T]>::connect`: renamed to join
+  --> $DIR/issue-84637-deprecated-associated-function.rs:10:27
+   |
+LL |     let _baz = ["a", "b"].connect(" ");
+   |                           ^^^^^^^
+   |
+help: replace the use of the deprecated method
+   |
+LL |     let _baz = ["a", "b"].join(" ");
+   |                           ~~~~
+
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-1.rs b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-1.rs
new file mode 100644
index 0000000000000..b0b9b6bbd2063
--- /dev/null
+++ b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-1.rs
@@ -0,0 +1,73 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+// A minimization of an ambiguity when using typenum. See
+// https://github.com/rust-lang/trait-system-refactor-initiative/issues/55
+// for more details.
+trait Id {
+    type Assoc: ?Sized;
+}
+impl<T: ?Sized> Id for T {
+    type Assoc = T;
+}
+
+trait WithAssoc<T: ?Sized> {
+    type Assoc: ?Sized;
+}
+
+
+struct Leaf;
+struct Wrapper<U: ?Sized>(U);
+
+impl<U: ?Sized> WithAssoc<U> for Leaf {
+    type Assoc = U;
+}
+
+impl<Ul: ?Sized, Ur: ?Sized> WithAssoc<Wrapper<Ur>> for Wrapper<Ul>
+where
+    Ul: WithAssoc<Ur>,
+{
+    type Assoc = <<Ul as WithAssoc<Ur>>::Assoc as Id>::Assoc;
+}
+
+fn bound<T: ?Sized, U: ?Sized, V: ?Sized>()
+where
+    T: WithAssoc<U, Assoc = V>,
+{
+}
+
+// normalize self type to `Wrapper<Leaf>`
+//   This succeeds, HOWEVER, instantiating the query response previously
+//   incremented the universe index counter.
+// equate impl headers:
+//      <Wrapper<Leaf> as WithAssoc<<Wrapper<Leaf> as Id>::Assoc>>
+//      <Wrapper<?2t> as WithAssoc<Wrapper<?3t>>>
+// ~> AliasRelate(<Wrapper<Leaf> as Id>::Assoc, Equate, Wrapper<?3t>)
+// add where bounds:
+// ~> Leaf: WithAssoc<?3t>
+// equate with assoc type:
+//      ?0t
+//      <Leaf as WithAssoc<?3t>>::Assoc as Id>::Assoc
+// ~> AliasRelate(
+//      <<Leaf as WithAssoc<?3t>>::Assoc as Id>::Assoc,
+//      Equate,
+//      <<Leaf as WithAssoc<?4t>>::Assoc as Id>::Assoc,
+//    )
+//
+// We do not reuse `?3t` during generalization because `?0t` cannot name `?4t` as we created
+// it after incrementing the universe index while normalizing the self type.
+//
+// evaluate_added_goals_and_make_query_response:
+//    AliasRelate(<Wrapper<Leaf> as Id>::Assoc, Equate, Wrapper<?3t>)
+//      YES, constrains ?3t to Leaf
+//    AliasRelate(
+//      <<Leaf as WithAssoc<Leaf>>::Assoc as Id>::Assoc,
+//      Equate,
+//      <<Leaf as WithAssoc<?4t>>::Assoc as Id>::Assoc,
+//    )
+//
+// Normalizing <<Leaf as WithAssoc<?4t>>::Assoc as Id>::Assoc then *correctly*
+// results in ambiguity.
+fn main() {
+    bound::<<Wrapper<Leaf> as Id>::Assoc, <Wrapper<Leaf> as Id>::Assoc, _>()
+}
diff --git a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.rs b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.rs
new file mode 100644
index 0000000000000..94d645a98592c
--- /dev/null
+++ b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.rs
@@ -0,0 +1,75 @@
+// compile-flags: -Ztrait-solver=next
+// known-bug: trait-system-refactor-initiative#60
+
+// Generalizing a projection containing an inference variable
+// which cannot be named by the `root_vid` can result in ambiguity.
+//
+// Because we do not decrement the universe index when exiting a forall,
+// this can cause unexpected failures.
+//
+// See generalize-proj-new-universe-index-1.rs for more details.
+
+// For this reproduction we need:
+// - an inference variable with a lower universe
+// - enter a binder to increment the current universe
+// - create a new inference variable which is constrained by proving a goal
+// - equate a projection containing the new variable with the first variable
+// - generalization creates yet another inference variable which is then
+//   part of an alias-relate, resulting this to fail with ambiguity.
+//
+// Because we need to enter the binder in-between the creation of the first
+// and second inference variable, this is easiest via
+// `assemble_candidates_after_normalizing_self_ty` because eagerly call
+// `try_evaluate_added_goals` there before creating the inference variables
+// for the impl parameters.
+trait Id {
+    type Assoc: ?Sized;
+}
+impl<T: ?Sized> Id for T {
+    type Assoc = T;
+}
+
+// By adding an higher ranked bound to the impl we currently
+// propagate this bound to the caller, forcing us to create a new
+// universe.
+trait IdHigherRankedBound {
+    type Assoc: ?Sized;
+}
+
+impl<T: ?Sized> IdHigherRankedBound for T
+where
+    for<'a> T: 'a,
+{
+    type Assoc = T;
+}
+
+trait WithAssoc<T: ?Sized> {
+    type Assoc: ?Sized;
+}
+
+
+struct Leaf;
+struct Wrapper<U: ?Sized>(U);
+struct Rigid;
+
+impl<U: ?Sized> WithAssoc<U> for Leaf {
+    type Assoc = U;
+}
+
+
+impl<Ur: ?Sized> WithAssoc<Wrapper<Ur>> for Rigid
+where
+    Leaf: WithAssoc<Ur>,
+{
+    type Assoc = <<Leaf as WithAssoc<Ur>>::Assoc as Id>::Assoc;
+}
+
+fn bound<T: ?Sized, U: ?Sized, V: ?Sized>()
+where
+    T: WithAssoc<U, Assoc = V>,
+{
+}
+
+fn main() {
+    bound::<<Rigid as IdHigherRankedBound>::Assoc, <Wrapper<Leaf> as Id>::Assoc, _>()
+}
diff --git a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr
new file mode 100644
index 0000000000000..9a8060133b819
--- /dev/null
+++ b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr
@@ -0,0 +1,9 @@
+error[E0282]: type annotations needed
+  --> $DIR/generalize-proj-new-universe-index-2.rs:74:5
+   |
+LL |     bound::<<Rigid as IdHigherRankedBound>::Assoc, <Wrapper<Leaf> as Id>::Assoc, _>()
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `V` declared on the function `bound`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.