diff --git a/Cargo.lock b/Cargo.lock
index 49a925d6e3780..95ac3136628c7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2142,9 +2142,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
 
 [[package]]
 name = "libc"
-version = "0.2.147"
+version = "0.2.148"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
 dependencies = [
  "rustc-std-workspace-core",
 ]
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 1d381175c752e..13826264a224c 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1201,25 +1201,35 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
         );
         return;
     }
-    for (span, _trivial, non_exhaustive) in field_infos {
-        if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
-            tcx.struct_span_lint_hir(
-                REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
-                tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
-                span,
-                "zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types",
-                |lint| {
-                    let note = if non_exhaustive {
-                        "is marked with `#[non_exhaustive]`"
-                    } else {
-                        "contains private fields"
-                    };
-                    let field_ty = tcx.def_path_str_with_args(def_id, args);
-                    lint
-                        .note(format!("this {descr} contains `{field_ty}`, which {note}, \
-                            and makes it not a breaking change to become non-zero-sized in the future."))
-                },
-            )
+    let mut prev_non_exhaustive_1zst = false;
+    for (span, _trivial, non_exhaustive_1zst) in field_infos {
+        if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive_1zst {
+            // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
+            // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
+            if non_trivial_count > 0 || prev_non_exhaustive_1zst {
+                tcx.struct_span_lint_hir(
+                    REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
+                    tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
+                    span,
+                    "zero-sized fields in `repr(transparent)` cannot \
+                    contain external non-exhaustive types",
+                    |lint| {
+                        let note = if non_exhaustive {
+                            "is marked with `#[non_exhaustive]`"
+                        } else {
+                            "contains private fields"
+                        };
+                        let field_ty = tcx.def_path_str_with_args(def_id, args);
+                        lint.note(format!(
+                            "this {descr} contains `{field_ty}`, which {note}, \
+                                and makes it not a breaking change to become \
+                                non-zero-sized in the future."
+                        ))
+                    },
+                )
+            } else {
+                prev_non_exhaustive_1zst = true;
+            }
         }
     }
 }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index edab7fe58baea..a14d09bbb7006 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2945,6 +2945,33 @@ impl<'tcx> Ty<'tcx> {
             _ => false,
         }
     }
+
+    pub fn is_known_rigid(self) -> bool {
+        match self.kind() {
+            Bool
+            | Char
+            | Int(_)
+            | Uint(_)
+            | Float(_)
+            | Adt(_, _)
+            | Foreign(_)
+            | Str
+            | Array(_, _)
+            | Slice(_)
+            | RawPtr(_)
+            | Ref(_, _, _)
+            | FnDef(_, _)
+            | FnPtr(_)
+            | Dynamic(_, _, _)
+            | Closure(_, _)
+            | Generator(_, _, _)
+            | GeneratorWitness(_)
+            | GeneratorWitnessMIR(_, _)
+            | Never
+            | Tuple(_) => true,
+            Error(_) | Infer(_) | Alias(_, _) | Param(_) | Bound(_, _) | Placeholder(_) => false,
+        }
+    }
 }
 
 /// Extra information about why we ended up with a particular variance.
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 135410691f1a1..7d754cbafa347 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -986,6 +986,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             }
                         }
 
+                        self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause);
+
                         // Return early if the trait is Debug or Display and the invocation
                         // originates within a standard library macro, because the output
                         // is otherwise overwhelming and unhelpful (see #85844 for an
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index d6ac720871588..a08ebe5a9ea8d 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -406,6 +406,14 @@ pub trait TypeErrCtxtExt<'tcx> {
         candidate_impls: &[ImplCandidate<'tcx>],
         span: Span,
     );
+
+    fn explain_hrtb_projection(
+        &self,
+        diag: &mut Diagnostic,
+        pred: ty::PolyTraitPredicate<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        cause: &ObligationCause<'tcx>,
+    );
 }
 
 fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
@@ -4027,6 +4035,71 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
         }
     }
+
+    fn explain_hrtb_projection(
+        &self,
+        diag: &mut Diagnostic,
+        pred: ty::PolyTraitPredicate<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        cause: &ObligationCause<'tcx>,
+    ) {
+        if pred.skip_binder().has_escaping_bound_vars() && pred.skip_binder().has_non_region_infer()
+        {
+            self.probe(|_| {
+                let ocx = ObligationCtxt::new(self);
+                let pred = self.instantiate_binder_with_placeholders(pred);
+                let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred);
+                ocx.register_obligation(Obligation::new(
+                    self.tcx,
+                    ObligationCause::dummy(),
+                    param_env,
+                    pred,
+                ));
+                if !ocx.select_where_possible().is_empty() {
+                    // encountered errors.
+                    return;
+                }
+
+                if let ObligationCauseCode::FunctionArgumentObligation {
+                    call_hir_id,
+                    arg_hir_id,
+                    parent_code: _,
+                } = cause.code()
+                {
+                    let arg_span = self.tcx.hir().span(*arg_hir_id);
+                    let mut sp: MultiSpan = arg_span.into();
+
+                    sp.push_span_label(
+                        arg_span,
+                        "the trait solver is unable to infer the \
+                        generic types that should be inferred from this argument",
+                    );
+                    sp.push_span_label(
+                        self.tcx.hir().span(*call_hir_id),
+                        "add turbofish arguments to this call to \
+                        specify the types manually, even if it's redundant",
+                    );
+                    diag.span_note(
+                        sp,
+                        "this is a known limitation of the trait solver that \
+                        will be lifted in the future",
+                    );
+                } else {
+                    let mut sp: MultiSpan = cause.span.into();
+                    sp.push_span_label(
+                        cause.span,
+                        "try adding turbofish arguments to this expression to \
+                        specify the types manually, even if it's redundant",
+                    );
+                    diag.span_note(
+                        sp,
+                        "this is a known limitation of the trait solver that \
+                        will be lifted in the future",
+                    );
+                }
+            });
+        }
+    }
 }
 
 /// Add a hint to add a missing borrow or remove an unnecessary one.
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index da2958bf56e8c..91f1c21310e30 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -141,11 +141,34 @@ fn resolve_associated_item<'tcx>(
                     false
                 }
             };
-
             if !eligible {
                 return Ok(None);
             }
 
+            // HACK: We may have overlapping `dyn Trait` built-in impls and
+            // user-provided blanket impls. Detect that case here, and return
+            // ambiguity.
+            //
+            // This should not affect totally monomorphized contexts, only
+            // resolve calls that happen polymorphically, such as the mir-inliner
+            // and const-prop (and also some lints).
+            let self_ty = rcvr_args.type_at(0);
+            if !self_ty.is_known_rigid() {
+                let predicates = tcx
+                    .predicates_of(impl_data.impl_def_id)
+                    .instantiate(tcx, impl_data.args)
+                    .predicates;
+                let sized_def_id = tcx.lang_items().sized_trait();
+                // If we find a `Self: Sized` bound on the item, then we know
+                // that `dyn Trait` can certainly never apply here.
+                if !predicates.into_iter().filter_map(ty::Clause::as_trait_clause).any(|clause| {
+                    Some(clause.def_id()) == sized_def_id
+                        && clause.skip_binder().self_ty() == self_ty
+                }) {
+                    return Ok(None);
+                }
+            }
+
             // Any final impl is required to define all associated items.
             if !leaf_def.item.defaultness(tcx).has_value() {
                 let guard = tcx.sess.delay_span_bug(
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index e2b97f983d22b..e8f642586cd7c 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
 panic_unwind = { path = "../panic_unwind", optional = true }
 panic_abort = { path = "../panic_abort" }
 core = { path = "../core", public = true }
-libc = { version = "0.2.146", default-features = false, features = ['rustc-dep-of-std'], public = true }
+libc = { version = "0.2.148", default-features = false, features = ['rustc-dep-of-std'], public = true }
 compiler_builtins = { version = "0.1.100" }
 profiler_builtins = { path = "../profiler_builtins", optional = true }
 unwind = { path = "../unwind" }
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 7c242d4d33453..2afec897a884f 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -324,8 +324,10 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> {
                     if libc::sched_getaffinity(0, mem::size_of::<libc::cpu_set_t>(), &mut set) == 0 {
                         let count = libc::CPU_COUNT(&set) as usize;
                         let count = count.min(quota);
-                        // SAFETY: affinity mask can't be empty and the quota gets clamped to a minimum of 1
-                        return Ok(NonZeroUsize::new_unchecked(count));
+                        // reported to occur on MIPS kernels older than our minimum supported kernel version for those targets
+                        let count = NonZeroUsize::new(count)
+                            .expect("CPU count must be > 0. This may be a bug in sched_getaffinity(); try upgrading the kernel.");
+                        return Ok(count);
                     }
                 }
             }
diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md
index 12a8b2b8db4b6..3b6883c0d557d 100644
--- a/src/doc/rustdoc/src/SUMMARY.md
+++ b/src/doc/rustdoc/src/SUMMARY.md
@@ -4,6 +4,7 @@
 - [Command-line arguments](command-line-arguments.md)
 - [How to read rustdoc output](how-to-read-rustdoc.md)
     - [In-doc settings](read-documentation/in-doc-settings.md)
+    - [Search](read-documentation/search.md)
 - [How to write documentation](how-to-write-documentation.md)
     - [What to include (and exclude)](write-documentation/what-to-include.md)
     - [The `#[doc]` attribute](write-documentation/the-doc-attribute.md)
diff --git a/src/doc/rustdoc/src/how-to-read-rustdoc.md b/src/doc/rustdoc/src/how-to-read-rustdoc.md
index cd6e29ffd6637..dade62c54234b 100644
--- a/src/doc/rustdoc/src/how-to-read-rustdoc.md
+++ b/src/doc/rustdoc/src/how-to-read-rustdoc.md
@@ -75,56 +75,11 @@ or the current item whose documentation is being displayed.
 ## The Theme Picker and Search Interface
 
 When viewing `rustdoc`'s output in a browser with JavaScript enabled,
-a dynamic interface appears at the top of the page composed of the search
-interface, help screen, and options.
-
-### The Search Interface
-
-Typing in the search bar instantly searches the available documentation for
-the string entered with a fuzzy matching algorithm that is tolerant of minor
-typos.
-
-By default, the search results given are "In Names",
-meaning that the fuzzy match is made against the names of items.
-Matching names are shown on the left, and the first few words of their
-descriptions are given on the right.
-By clicking an item, you will navigate to its particular documentation.
-
-There are two other sets of results, shown as tabs in the search results pane.
-"In Parameters" shows matches for the string in the types of parameters to
-functions, and "In Return Types" shows matches in the return types of functions.
-Both are very useful when looking for a function whose name you can't quite
-bring to mind when you know the type you have or want.
-
-Names in the search interface can be prefixed with an item type followed by a
-colon (such as `mod:`) to restrict the results to just that kind of item. Also,
-searching for `println!` will search for a macro named `println`, just like
-searching for `macro:println` does.
-
-Function signature searches can query generics, wrapped in angle brackets, and
-traits are normalized like types in the search engine. For example, a function
-with the signature `fn my_function<I: Iterator<Item=u32>>(input: I) -> usize`
-can be matched with the following queries:
-
-* `Iterator<u32> -> usize`
-* `trait:Iterator<primitive:u32> -> primitive:usize`
-* `Iterator -> usize`
-
-Generics and function parameters are order-agnostic, but sensitive to nesting
-and number of matches. For example, a function with the signature
-`fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>`
-will match these queries:
-
-* `Read -> Result<Vec<u8>, Error>`
-* `Read -> Result<Error, Vec>`
-* `Read -> Result<Vec<u8>>`
-
-But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
-
-Function signature searches also support arrays and slices. The explicit name
-`primitive:slice<u8>` and `primitive:array<u8>` can be used to match a slice
-or array of bytes, while square brackets `[u8]` will match either one. Empty
-square brackets, `[]`, will match any slice regardless of what it contains.
+a dynamic interface appears at the top of the page composed of the [search]
+interface, help screen, and [options].
+
+[options]: read-documentation/in-doc-settings.html
+[search]: read-documentation/search.md
 
 Paths are supported as well, you can look for `Vec::new` or `Option::Some` or
 even `module::module_child::another_child::struct::field`. Whitespace characters
diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md
new file mode 100644
index 0000000000000..56a5016d0cee0
--- /dev/null
+++ b/src/doc/rustdoc/src/read-documentation/search.md
@@ -0,0 +1,237 @@
+# Rustdoc search
+
+Typing in the search bar instantly searches the available documentation,
+matching either the name and path of an item, or a function's approximate
+type signature.
+
+## Search By Name
+
+To search by the name of an item (items include modules, types, traits,
+functions, and macros), write its name or path. As a special case, the parts
+of a path that normally get divided by `::` double colons can instead be
+separated by spaces. For example:
+
+  * [`vec new`] and [`vec::new`] both show the function `std::vec::Vec::new`
+    as a result.
+  * [`vec`], [`vec vec`], [`std::vec`], and [`std::vec::Vec`] all include the struct
+    `std::vec::Vec` itself in the results (and all but the last one also
+    include the module in the results).
+
+[`vec new`]: ../../std/vec/struct.Vec.html?search=vec%20new&filter-crate=std
+[`vec::new`]: ../../std/vec/struct.Vec.html?search=vec::new&filter-crate=std
+[`vec`]: ../../std/vec/struct.Vec.html?search=vec&filter-crate=std
+[`vec vec`]: ../../std/vec/struct.Vec.html?search=vec%20vec&filter-crate=std
+[`std::vec`]: ../../std/vec/struct.Vec.html?search=std::vec&filter-crate=std
+[`std::vec::Vec`]: ../../std/vec/struct.Vec.html?search=std::vec::Vec&filter-crate=std
+[`std::vec::Vec`]: ../../std/vec/struct.Vec.html?search=std::vec::Vec&filter-crate=std
+
+As a quick way to trim down the list of results, there's a drop-down selector
+below the search input, labeled "Results in \[std\]". Clicking it can change
+which crate is being searched.
+
+Rustdoc uses a fuzzy matching function that can tolerate typos for this,
+though it's based on the length of the name that's typed in, so a good example
+of how this works would be [`HahsMap`]. To avoid this, wrap the item in quotes,
+searching for `"HahsMap"` (in this example, no results will be returned).
+
+[`HahsMap`]: ../../std/collections/struct.HashMap.html?search=HahsMap&filter-crate=std
+
+### Tabs in the Search By Name interface
+
+In fact, using [`HahsMap`] again as the example, it tells you that you're
+using "In Names" by default, but also lists two other tabs below the crate
+drop-down: "In Parameters" and "In Return Types".
+
+These two tabs are lists of functions, defined on the closest matching type
+to the search (for `HahsMap`, it loudly auto-corrects to `hashmap`). This
+auto-correct only kicks in if nothing is found that matches the literal.
+
+These tabs are not just methods. For example, searching the alloc crate for
+[`Layout`] also lists functions that accept layouts even though they're
+methods on the allocator or free functions.
+
+[`Layout`]: ../../alloc/index.html?search=Layout&filter-crate=alloc
+
+## Searching By Type Signature for functions
+
+If you know more specifically what the function you want to look at does,
+Rustdoc can search by more than one type at once in the parameters and return
+value. Multiple parameters are separated by `,` commas, and the return value
+is written with after a `->` arrow.
+
+Before describing the syntax in more detail, here's a few sample searches of
+the standard library and functions that are included in the results list:
+
+| Query | Results |
+|-------|--------|
+| [`usize -> vec`][] | `slice::repeat` and `Vec::with_capacity` |
+| [`vec, vec -> bool`][] | `Vec::eq` |
+| [`option<T>, fnonce -> option<U>`][] | `Option::map` and `Option::and_then` |
+| [`option<T>, fnonce -> option<T>`][] | `Option::filter` and `Option::inspect` |
+| [`option -> default`][] | `Option::unwrap_or_default` |
+| [`stdout, [u8]`][stdoutu8] | `Stdout::write` |
+| [`any -> !`][] | `panic::panic_any` |
+| [`vec::intoiter<T> -> [T]`][iterasslice] | `IntoIter::as_slice` and `IntoIter::next_chunk` |
+
+[`usize -> vec`]: ../../std/vec/struct.Vec.html?search=usize%20-%3E%20vec&filter-crate=std
+[`vec, vec -> bool`]: ../../std/vec/struct.Vec.html?search=vec,%20vec%20-%3E%20bool&filter-crate=std
+[`option<T>, fnonce -> option<U>`]: ../../std/vec/struct.Vec.html?search=option<T>%2C%20fnonce%20->%20option<U>&filter-crate=std
+[`option<T>, fnonce -> option<T>`]: ../../std/vec/struct.Vec.html?search=option<T>%2C%20fnonce%20->%20option<T>&filter-crate=std
+[`option -> default`]: ../../std/vec/struct.Vec.html?search=option%20-%3E%20default&filter-crate=std
+[`any -> !`]: ../../std/vec/struct.Vec.html?search=any%20-%3E%20!&filter-crate=std
+[stdoutu8]: ../../std/vec/struct.Vec.html?search=stdout%2C%20[u8]&filter-crate=std
+[iterasslice]: ../../std/vec/struct.Vec.html?search=vec%3A%3Aintoiter<T>%20->%20[T]&filter-crate=std
+
+### How type-based search works
+
+In a complex type-based search, Rustdoc always treats every item's name as literal.
+If a name is used and nothing in the docs matches the individual item, such as
+a typo-ed [`uize -> vec`][] search, the item `uize` is treated as a generic
+type parameter (resulting in `vec::from` and other generic vec constructors).
+
+[`uize -> vec`]: ../../std/vec/struct.Vec.html?search=uize%20-%3E%20vec&filter-crate=std
+
+After deciding which items are type parameters and which are actual types, it
+then searches by matching up the function parameters (written before the `->`)
+and the return types (written after the `->`). Type matching is order-agnostic,
+and allows items to be left out of the query, but items that are present in the
+query must be present in the function for it to match.
+
+Function signature searches can query generics, wrapped in angle brackets, and
+traits will be normalized like types in the search engine if no type parameters
+match them. For example, a function with the signature
+`fn my_function<I: Iterator<Item=u32>>(input: I) -> usize`
+can be matched with the following queries:
+
+* `Iterator<u32> -> usize`
+* `Iterator -> usize`
+
+Generics and function parameters are order-agnostic, but sensitive to nesting
+and number of matches. For example, a function with the signature
+`fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>`
+will match these queries:
+
+* `Read -> Result<Vec<u8>, Error>`
+* `Read -> Result<Error, Vec>`
+* `Read -> Result<Vec<u8>>`
+
+But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
+
+Function signature searches also support arrays and slices. The explicit name
+`primitive:slice<u8>` and `primitive:array<u8>` can be used to match a slice
+or array of bytes, while square brackets `[u8]` will match either one. Empty
+square brackets, `[]`, will match any slice or array regardless of what
+it contains, while a slice with a type parameter, like `[T]`, will only match
+functions that actually operate on generic slices.
+
+### Limitations and quirks of type-based search
+
+Type-based search is still a buggy, experimental, work-in-progress feature.
+Most of these limitations should be addressed in future version of Rustdoc.
+
+  * There's no way to write trait constraints on generic parameters.
+    You can name traits directly, and if there's a type parameter
+    with that bound, it'll match, but `option<T> -> T where T: Default`
+    cannot be precisely searched for (use `option<Default> -> Default`).
+
+  * Type parameters match type parameters, such that `Option<A>` matches
+    `Option<T>`, but never match concrete types in function signatures.
+    A trait named as if it were a type, such as `Option<Read>`, will match
+    a type parameter constrained by that trait, such as
+    `Option<T> where T: Read`, as well as matching `dyn Trait` and
+    `impl Trait`.
+
+  * `impl Trait` in argument position is treated exactly like a type
+    parameter, but in return position it will not match type parameters.
+
+  * Any type named in a complex type-based search will be assumed to be a
+    type parameter if nothing matching the name exactly is found. If you
+    want to force a type parameter, write `generic:T` and it will be used
+    as a type parameter even if a matching name is found. If you know
+    that you don't want a type parameter, you can force it to match
+    something else by giving it a different prefix like `struct:T`.
+
+  * It's impossible to search for references, pointers, or tuples. The
+    wrapped types can be searched for, so a function that takes `&File` can
+    be found with `File`, but you'll get a parse error when typing an `&`
+    into the search field. Similarly, `Option<(T, U)>` can be matched with
+    `Option<T, U>`, but `(` will give a parse error.
+
+  * Searching for lifetimes is not supported.
+
+  * It's impossible to search for closures based on their parameters or
+    return values.
+
+  * It's impossible to search based on the length of an array.
+
+## Item filtering
+
+Names in the search interface can be prefixed with an item type followed by a
+colon (such as `mod:`) to restrict the results to just that kind of item. Also,
+searching for `println!` will search for a macro named `println`, just like
+searching for `macro:println` does. The complete list of available filters is
+given under the <kbd>?</kbd> Help area, and in the detailed syntax below.
+
+Item filters can be used in both name-based and type signature-based searches.
+
+## Search query syntax
+
+```text
+ident = *(ALPHA / DIGIT / "_")
+path = ident *(DOUBLE-COLON ident) [!]
+slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
+arg = [type-filter *WS COLON *WS] (path [generics] / slice / [!])
+type-sep = COMMA/WS *(COMMA/WS)
+nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
+generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
+            CLOSE-ANGLE-BRACKET
+return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
+
+exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
+type-search = [ nonempty-arg-list ] [ return-args ]
+
+query = *WS (exact-search / type-search) *WS
+
+type-filter = (
+    "mod" /
+    "externcrate" /
+    "import" /
+    "struct" /
+    "enum" /
+    "fn" /
+    "type" /
+    "static" /
+    "trait" /
+    "impl" /
+    "tymethod" /
+    "method" /
+    "structfield" /
+    "variant" /
+    "macro" /
+    "primitive" /
+    "associatedtype" /
+    "constant" /
+    "associatedconstant" /
+    "union" /
+    "foreigntype" /
+    "keyword" /
+    "existential" /
+    "attr" /
+    "derive" /
+    "traitalias" /
+    "generic")
+
+OPEN-ANGLE-BRACKET = "<"
+CLOSE-ANGLE-BRACKET = ">"
+OPEN-SQUARE-BRACKET = "["
+CLOSE-SQUARE-BRACKET = "]"
+COLON = ":"
+DOUBLE-COLON = "::"
+QUOTE = %x22
+COMMA = ","
+RETURN-ARROW = "->"
+
+ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+DIGIT = %x30-39
+WS = %x09 / " "
+```
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index b276745f3170b..ef7794cc41e2c 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1639,10 +1639,6 @@ impl Type {
         matches!(self, Type::Generic(_))
     }
 
-    pub(crate) fn is_impl_trait(&self) -> bool {
-        matches!(self, Type::ImplTrait(_))
-    }
-
     pub(crate) fn is_unit(&self) -> bool {
         matches!(self, Type::Tuple(v) if v.is_empty())
     }
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index f70f59d3be389..3e671a64b54f7 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -101,7 +101,7 @@ pub(crate) struct IndexItem {
     pub(crate) path: String,
     pub(crate) desc: String,
     pub(crate) parent: Option<DefId>,
-    pub(crate) parent_idx: Option<usize>,
+    pub(crate) parent_idx: Option<isize>,
     pub(crate) search_type: Option<IndexItemFunctionType>,
     pub(crate) aliases: Box<[Symbol]>,
     pub(crate) deprecation: Option<Deprecation>,
@@ -122,7 +122,10 @@ impl Serialize for RenderType {
         let id = match &self.id {
             // 0 is a sentinel, everything else is one-indexed
             None => 0,
-            Some(RenderTypeId::Index(idx)) => idx + 1,
+            // concrete type
+            Some(RenderTypeId::Index(idx)) if *idx >= 0 => idx + 1,
+            // generic type parameter
+            Some(RenderTypeId::Index(idx)) => *idx,
             _ => panic!("must convert render types to indexes before serializing"),
         };
         if let Some(generics) = &self.generics {
@@ -140,7 +143,7 @@ impl Serialize for RenderType {
 pub(crate) enum RenderTypeId {
     DefId(DefId),
     Primitive(clean::PrimitiveType),
-    Index(usize),
+    Index(isize),
 }
 
 /// Full type of functions/methods in the search index.
@@ -148,6 +151,7 @@ pub(crate) enum RenderTypeId {
 pub(crate) struct IndexItemFunctionType {
     inputs: Vec<RenderType>,
     output: Vec<RenderType>,
+    where_clause: Vec<Vec<RenderType>>,
 }
 
 impl Serialize for IndexItemFunctionType {
@@ -170,10 +174,17 @@ impl Serialize for IndexItemFunctionType {
                 _ => seq.serialize_element(&self.inputs)?,
             }
             match &self.output[..] {
-                [] => {}
+                [] if self.where_clause.is_empty() => {}
                 [one] if one.generics.is_none() => seq.serialize_element(one)?,
                 _ => seq.serialize_element(&self.output)?,
             }
+            for constraint in &self.where_clause {
+                if let [one] = &constraint[..] && one.generics.is_none() {
+                    seq.serialize_element(one)?;
+                } else {
+                    seq.serialize_element(constraint)?;
+                }
+            }
             seq.end()
         }
     }
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 145c7d18dd013..78c443b2257d0 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -68,16 +68,16 @@ pub(crate) fn build_index<'tcx>(
     // Reduce `DefId` in paths into smaller sequential numbers,
     // and prune the paths that do not appear in the index.
     let mut lastpath = "";
-    let mut lastpathid = 0usize;
+    let mut lastpathid = 0isize;
 
     // First, on function signatures
     let mut search_index = std::mem::replace(&mut cache.search_index, Vec::new());
     for item in search_index.iter_mut() {
         fn insert_into_map<F: std::hash::Hash + Eq>(
             ty: &mut RenderType,
-            map: &mut FxHashMap<F, usize>,
+            map: &mut FxHashMap<F, isize>,
             itemid: F,
-            lastpathid: &mut usize,
+            lastpathid: &mut isize,
             crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
             item_type: ItemType,
             path: &[Symbol],
@@ -97,9 +97,9 @@ pub(crate) fn build_index<'tcx>(
         fn convert_render_type(
             ty: &mut RenderType,
             cache: &mut Cache,
-            itemid_to_pathid: &mut FxHashMap<ItemId, usize>,
-            primitives: &mut FxHashMap<Symbol, usize>,
-            lastpathid: &mut usize,
+            itemid_to_pathid: &mut FxHashMap<ItemId, isize>,
+            primitives: &mut FxHashMap<Symbol, isize>,
+            lastpathid: &mut isize,
             crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
         ) {
             if let Some(generics) = &mut ty.generics {
@@ -173,6 +173,18 @@ pub(crate) fn build_index<'tcx>(
                     &mut crate_paths,
                 );
             }
+            for constraint in &mut search_type.where_clause {
+                for trait_ in &mut constraint[..] {
+                    convert_render_type(
+                        trait_,
+                        cache,
+                        &mut itemid_to_pathid,
+                        &mut primitives,
+                        &mut lastpathid,
+                        &mut crate_paths,
+                    );
+                }
+            }
         }
     }
 
@@ -402,7 +414,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
     impl_generics: Option<&(clean::Type, clean::Generics)>,
     cache: &Cache,
 ) -> Option<IndexItemFunctionType> {
-    let (mut inputs, mut output) = match *item.kind {
+    let (mut inputs, mut output, where_clause) = match *item.kind {
         clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
         clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
         clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
@@ -412,7 +424,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
     inputs.retain(|a| a.id.is_some() || a.generics.is_some());
     output.retain(|a| a.id.is_some() || a.generics.is_some());
 
-    Some(IndexItemFunctionType { inputs, output })
+    Some(IndexItemFunctionType { inputs, output, where_clause })
 }
 
 fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
@@ -432,96 +444,48 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
         clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
             get_index_type_id(type_)
         }
-        // The type parameters are converted to generics in `add_generics_and_bounds_as_types`
+        // The type parameters are converted to generics in `simplify_fn_type`
         clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
         clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
+        clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)),
         // Not supported yet
         clean::BareFunction(_)
         | clean::Generic(_)
         | clean::ImplTrait(_)
-        | clean::Tuple(_)
         | clean::QPath { .. }
         | clean::Infer => None,
     }
 }
 
-/// The point of this function is to replace bounds with types.
+#[derive(Clone, Copy, Eq, Hash, PartialEq)]
+enum SimplifiedParam {
+    // other kinds of type parameters are identified by their name
+    Symbol(Symbol),
+    // every argument-position impl trait is its own type parameter
+    Anonymous(isize),
+}
+
+/// The point of this function is to lower generics and types into the simplified form that the
+/// frontend search engine can use.
 ///
-/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
-/// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
+/// For example, `[T, U, i32]]` where you have the bounds: `T: Display, U: Option<T>` will return
+/// `[-1, -2, i32] where -1: Display, -2: Option<-1>`. If a type parameter has no traid bound, it
+/// will still get a number. If a constraint is present but not used in the actual types, it will
+/// not be added to the map.
 ///
-/// Important note: It goes through generics recursively. So if you have
-/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
-#[instrument(level = "trace", skip(tcx, res, cache))]
-fn add_generics_and_bounds_as_types<'tcx, 'a>(
+/// This function also works recursively.
+#[instrument(level = "trace", skip(tcx, res, rgen, cache))]
+fn simplify_fn_type<'tcx, 'a>(
     self_: Option<&'a Type>,
     generics: &Generics,
     arg: &'a Type,
     tcx: TyCtxt<'tcx>,
     recurse: usize,
     res: &mut Vec<RenderType>,
+    rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+    is_return: bool,
     cache: &Cache,
 ) {
-    fn insert_ty(res: &mut Vec<RenderType>, ty: Type, mut generics: Vec<RenderType>) {
-        // generics and impl trait are both identified by their generics,
-        // rather than a type name itself
-        let anonymous = ty.is_full_generic() || ty.is_impl_trait();
-        let generics_empty = generics.is_empty();
-
-        if anonymous {
-            if generics_empty {
-                // This is a type parameter with no trait bounds (for example: `T` in
-                // `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
-                // with an empty type with an empty name. Let's just discard it.
-                return;
-            } else if generics.len() == 1 {
-                // In this case, no need to go through an intermediate state if the type parameter
-                // contains only one trait bound.
-                //
-                // For example:
-                //
-                // `fn foo<T: Display>(r: Option<T>) {}`
-                //
-                // In this case, it would contain:
-                //
-                // ```
-                // [{
-                //     name: "option",
-                //     generics: [{
-                //         name: "",
-                //         generics: [
-                //             name: "Display",
-                //             generics: []
-                //         }]
-                //     }]
-                // }]
-                // ```
-                //
-                // After removing the intermediate (unnecessary) type parameter, it'll become:
-                //
-                // ```
-                // [{
-                //     name: "option",
-                //     generics: [{
-                //         name: "Display",
-                //         generics: []
-                //     }]
-                // }]
-                // ```
-                //
-                // To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
-                // need to keep it as is!
-                res.push(generics.pop().unwrap());
-                return;
-            }
-        }
-        let index_ty = get_index_type(&ty, generics);
-        if index_ty.id.is_none() && generics_empty {
-            return;
-        }
-        res.push(index_ty);
-    }
-
     if recurse >= 10 {
         // FIXME: remove this whole recurse thing when the recursion bug is fixed
         // See #59502 for the original issue.
@@ -548,88 +512,126 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
     // for its bounds.
     if let Type::Generic(arg_s) = *arg {
         // First we check if the bounds are in a `where` predicate...
+        let mut type_bounds = Vec::new();
         for where_pred in generics.where_predicates.iter().filter(|g| match g {
             WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s,
             _ => false,
         }) {
-            let mut ty_generics = Vec::new();
             let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
             for bound in bounds.iter() {
                 if let Some(path) = bound.get_trait_path() {
                     let ty = Type::Path { path };
-                    add_generics_and_bounds_as_types(
+                    simplify_fn_type(
                         self_,
                         generics,
                         &ty,
                         tcx,
                         recurse + 1,
-                        &mut ty_generics,
+                        &mut type_bounds,
+                        rgen,
+                        is_return,
                         cache,
                     );
                 }
             }
-            insert_ty(res, arg.clone(), ty_generics);
         }
         // Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
         if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
-            let mut ty_generics = Vec::new();
             for bound in bound.get_bounds().unwrap_or(&[]) {
                 if let Some(path) = bound.get_trait_path() {
                     let ty = Type::Path { path };
-                    add_generics_and_bounds_as_types(
+                    simplify_fn_type(
                         self_,
                         generics,
                         &ty,
                         tcx,
                         recurse + 1,
-                        &mut ty_generics,
+                        &mut type_bounds,
+                        rgen,
+                        is_return,
                         cache,
                     );
                 }
             }
-            insert_ty(res, arg.clone(), ty_generics);
+        }
+        if let Some((idx, _)) = rgen.get(&SimplifiedParam::Symbol(arg_s)) {
+            res.push(RenderType { id: Some(RenderTypeId::Index(*idx)), generics: None });
+        } else {
+            let idx = -isize::try_from(rgen.len() + 1).unwrap();
+            rgen.insert(SimplifiedParam::Symbol(arg_s), (idx, type_bounds));
+            res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
         }
     } else if let Type::ImplTrait(ref bounds) = *arg {
-        let mut ty_generics = Vec::new();
+        let mut type_bounds = Vec::new();
         for bound in bounds {
             if let Some(path) = bound.get_trait_path() {
                 let ty = Type::Path { path };
-                add_generics_and_bounds_as_types(
+                simplify_fn_type(
                     self_,
                     generics,
                     &ty,
                     tcx,
                     recurse + 1,
-                    &mut ty_generics,
+                    &mut type_bounds,
+                    rgen,
+                    is_return,
                     cache,
                 );
             }
         }
-        insert_ty(res, arg.clone(), ty_generics);
+        if is_return && !type_bounds.is_empty() {
+            // In parameter position, `impl Trait` is a unique thing.
+            res.push(RenderType { id: None, generics: Some(type_bounds) });
+        } else {
+            // In parameter position, `impl Trait` is the same as an unnamed generic parameter.
+            let idx = -isize::try_from(rgen.len() + 1).unwrap();
+            rgen.insert(SimplifiedParam::Anonymous(idx), (idx, type_bounds));
+            res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
+        }
     } else if let Type::Slice(ref ty) = *arg {
         let mut ty_generics = Vec::new();
-        add_generics_and_bounds_as_types(
+        simplify_fn_type(
             self_,
             generics,
             &ty,
             tcx,
             recurse + 1,
             &mut ty_generics,
+            rgen,
+            is_return,
             cache,
         );
-        insert_ty(res, arg.clone(), ty_generics);
+        res.push(get_index_type(arg, ty_generics));
     } else if let Type::Array(ref ty, _) = *arg {
         let mut ty_generics = Vec::new();
-        add_generics_and_bounds_as_types(
+        simplify_fn_type(
             self_,
             generics,
             &ty,
             tcx,
             recurse + 1,
             &mut ty_generics,
+            rgen,
+            is_return,
             cache,
         );
-        insert_ty(res, arg.clone(), ty_generics);
+        res.push(get_index_type(arg, ty_generics));
+    } else if let Type::Tuple(ref tys) = *arg {
+        let mut ty_generics = Vec::new();
+        for ty in tys {
+            simplify_fn_type(
+                self_,
+                generics,
+                &ty,
+                tcx,
+                recurse + 1,
+                &mut ty_generics,
+                rgen,
+                is_return,
+                cache,
+            );
+        }
+        res.push(get_index_type(arg, ty_generics));
     } else {
         // This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
         // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
@@ -639,18 +641,26 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
         let mut ty_generics = Vec::new();
         if let Some(arg_generics) = arg.generics() {
             for gen in arg_generics.iter() {
-                add_generics_and_bounds_as_types(
+                simplify_fn_type(
                     self_,
                     generics,
                     gen,
                     tcx,
                     recurse + 1,
                     &mut ty_generics,
+                    rgen,
+                    is_return,
                     cache,
                 );
             }
         }
-        insert_ty(res, arg.clone(), ty_generics);
+        let id = get_index_type_id(&arg);
+        if id.is_some() || !ty_generics.is_empty() {
+            res.push(RenderType {
+                id,
+                generics: if ty_generics.is_empty() { None } else { Some(ty_generics) },
+            });
+        }
     }
 }
 
@@ -663,7 +673,7 @@ fn get_fn_inputs_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_generics: Option<&(clean::Type, clean::Generics)>,
     cache: &Cache,
-) -> (Vec<RenderType>, Vec<RenderType>) {
+) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
     let decl = &func.decl;
 
     let combined_generics;
@@ -689,21 +699,27 @@ fn get_fn_inputs_and_outputs<'tcx>(
         (None, &func.generics)
     };
 
-    let mut all_types = Vec::new();
+    let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
+
+    let mut arg_types = Vec::new();
     for arg in decl.inputs.values.iter() {
-        let mut args = Vec::new();
-        add_generics_and_bounds_as_types(self_, generics, &arg.type_, tcx, 0, &mut args, cache);
-        if !args.is_empty() {
-            all_types.extend(args);
-        } else {
-            all_types.push(get_index_type(&arg.type_, vec![]));
-        }
+        simplify_fn_type(
+            self_,
+            generics,
+            &arg.type_,
+            tcx,
+            0,
+            &mut arg_types,
+            &mut rgen,
+            false,
+            cache,
+        );
     }
 
     let mut ret_types = Vec::new();
-    add_generics_and_bounds_as_types(self_, generics, &decl.output, tcx, 0, &mut ret_types, cache);
-    if ret_types.is_empty() {
-        ret_types.push(get_index_type(&decl.output, vec![]));
-    }
-    (all_types, ret_types)
+    simplify_fn_type(self_, generics, &decl.output, tcx, 0, &mut ret_types, &mut rgen, true, cache);
+
+    let mut simplified_params = rgen.into_values().collect::<Vec<_>>();
+    simplified_params.sort_by_key(|(idx, _)| -idx);
+    (arg_types, ret_types, simplified_params.into_iter().map(|(_idx, traits)| traits).collect())
 }
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index f697abd07765a..c7811b43d1641 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -9,7 +9,7 @@ function initSearch(searchIndex){}
 /**
  * @typedef {{
  *     name: string,
- *     id: integer,
+ *     id: integer|null,
  *     fullPath: Array<string>,
  *     pathWithoutLast: Array<string>,
  *     pathLast: string,
@@ -37,6 +37,7 @@ let ParserState;
  *     args: Array<QueryElement>,
  *     returned: Array<QueryElement>,
  *     foundElems: number,
+ *     totalElems: number,
  *     literalSearch: boolean,
  *     corrections: Array<{from: string, to: integer}>,
  * }}
@@ -103,7 +104,7 @@ let ResultObject;
  *
  *     fn something() -> Result<usize, usize>
  *
- * If output was allowed to be any RawFunctionType, it would look like this
+ * If output was allowed to be any RawFunctionType, it would look like thi
  *
  *     [[], [50, [3, 3]]]
  *
@@ -113,10 +114,56 @@ let ResultObject;
  * in favor of the pair of types interpretation. This is why the `(number|Array<RawFunctionType>)`
  * is used instead of `(RawFunctionType|Array<RawFunctionType>)`.
  *
+ * The output can be skipped if it's actually unit and there's no type constraints. If thi
+ * function accepts constrained generics, then the output will be unconditionally emitted, and
+ * after it will come a list of trait constraints. The position of the item in the list will
+ * determine which type parameter it is. For example:
+ *
+ *     [1, 2, 3, 4, 5]
+ *      ^  ^  ^  ^  ^
+ *      |  |  |  |  - generic parameter (-3) of trait 5
+ *      |  |  |  - generic parameter (-2) of trait 4
+ *      |  |  - generic parameter (-1) of trait 3
+ *      |  - this function returns a single value (type 2)
+ *      - this function takes a single input parameter (type 1)
+ *
+ * Or, for a less contrived version:
+ *
+ *     [[[4, -1], 3], [[5, -1]], 11]
+ *      -^^^^^^^----   ^^^^^^^   ^^
+ *       |        |    |          - generic parameter, roughly `where -1: 11`
+ *       |        |    |            since -1 is the type parameter and 11 the trait
+ *       |        |    - function output 5<-1>
+ *       |        - the overall function signature is something like
+ *       |          `fn(4<-1>, 3) -> 5<-1> where -1: 11`
+ *       - function input, corresponds roughly to 4<-1>
+ *         4 is an index into the `p` array for a type
+ *         -1 is the generic parameter, given by 11
+ *
+ * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like
+ * function inputs and outputs:
+ *
+ *     [-1, -1, [4, 3]]
+ *              ^^^^^^ where -1: 4 + 3
+ *
+ * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array
+ * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in
+ * favor of `4 + 3`:
+ *
+ *     [-1, -1, [[4, 3]]]
+ *              ^^^^^^^^ where -1: 4 + 3
+ *
+ *     [-1, -1, [5, [4, 3]]]
+ *              ^^^^^^^^^^^ where -1: 5, -2: 4 + 3
+ *
+ * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i
+ * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0.
+ *
  * @typedef {(
  *     0 |
  *     [(number|Array<RawFunctionType>)] |
- *     [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)]
+ *     [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)] |
+ *     Array<(number|Array<RawFunctionType>)>
  * )}
  */
 let RawFunctionSearchType;
@@ -136,6 +183,7 @@ let RawFunctionType;
  * @typedef {{
  *     inputs: Array<FunctionType>,
  *     output: Array<FunctionType>,
+ *     where_clause: Array<Array<FunctionType>>,
  * }}
  */
 let FunctionSearchType;
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 0e270bbcc4028..407bf5f2c5fe1 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -3,6 +3,17 @@
 
 "use strict";
 
+// polyfill
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced
+if (!Array.prototype.toSpliced) {
+    // Can't use arrow functions, because we want `this`
+    Array.prototype.toSpliced = function() {
+        const me = this.slice();
+        Array.prototype.splice.apply(me, arguments);
+        return me;
+    };
+}
+
 (function() {
 // This mapping table should match the discriminants of
 // `rustdoc::formats::item_type::ItemType` type in Rust.
@@ -33,6 +44,7 @@ const itemTypes = [
     "attr",
     "derive",
     "traitalias",
+    "generic",
 ];
 
 const longItemTypes = [
@@ -67,6 +79,7 @@ const longItemTypes = [
 // used for special search precedence
 const TY_PRIMITIVE = itemTypes.indexOf("primitive");
 const TY_KEYWORD = itemTypes.indexOf("keyword");
+const TY_GENERIC = itemTypes.indexOf("generic");
 const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
 
 function hasOwnPropertyRustdoc(obj, property) {
@@ -252,7 +265,7 @@ function initSearch(rawSearchIndex) {
 
     /**
      * Add an item to the type Name->ID map, or, if one already exists, use it.
-     * Returns the number. If name is "" or null, return -1 (pure generic).
+     * Returns the number. If name is "" or null, return null (pure generic).
      *
      * This is effectively string interning, so that function matching can be
      * done more quickly. Two types with the same name but different item kinds
@@ -264,7 +277,7 @@ function initSearch(rawSearchIndex) {
      */
     function buildTypeMapIndex(name) {
         if (name === "" || name === null) {
-            return -1;
+            return null;
         }
 
         if (typeNameIdMap.has(name)) {
@@ -489,7 +502,7 @@ function initSearch(rawSearchIndex) {
             }
             return {
                 name: "never",
-                id: -1,
+                id: null,
                 fullPath: ["never"],
                 pathWithoutLast: [],
                 pathLast: "never",
@@ -531,7 +544,7 @@ function initSearch(rawSearchIndex) {
         }
         return {
             name: name.trim(),
-            id: -1,
+            id: null,
             fullPath: pathSegments,
             pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
             pathLast: pathSegments[pathSegments.length - 1],
@@ -660,7 +673,7 @@ function initSearch(rawSearchIndex) {
             }
             elems.push({
                 name: "[]",
-                id: -1,
+                id: null,
                 fullPath: ["[]"],
                 pathWithoutLast: [],
                 pathLast: "[]",
@@ -971,9 +984,13 @@ function initSearch(rawSearchIndex) {
             returned: [],
             // Total number of "top" elements (does not include generics).
             foundElems: 0,
+            // Total number of elements (includes generics).
+            totalElems: 0,
             literalSearch: false,
             error: null,
             correction: null,
+            proposeCorrectionFrom: null,
+            proposeCorrectionTo: null,
         };
     }
 
@@ -1014,64 +1031,10 @@ function initSearch(rawSearchIndex) {
     /**
      * Parses the query.
      *
-     * The supported syntax by this parser is as follow:
-     *
-     * ident = *(ALPHA / DIGIT / "_")
-     * path = ident *(DOUBLE-COLON/{WS} ident) [!]
-     * slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
-     * arg = [type-filter *WS COLON *WS] (path [generics] / slice)
-     * type-sep = *WS COMMA *(COMMA)
-     * nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
-     * generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
-     *            CLOSE-ANGLE-BRACKET
-     * return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
-     *
-     * exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
-     * type-search = [ nonempty-arg-list ] [ return-args ]
-     *
-     * query = *WS (exact-search / type-search) *WS
-     *
-     * type-filter = (
-     *     "mod" /
-     *     "externcrate" /
-     *     "import" /
-     *     "struct" /
-     *     "enum" /
-     *     "fn" /
-     *     "type" /
-     *     "static" /
-     *     "trait" /
-     *     "impl" /
-     *     "tymethod" /
-     *     "method" /
-     *     "structfield" /
-     *     "variant" /
-     *     "macro" /
-     *     "primitive" /
-     *     "associatedtype" /
-     *     "constant" /
-     *     "associatedconstant" /
-     *     "union" /
-     *     "foreigntype" /
-     *     "keyword" /
-     *     "existential" /
-     *     "attr" /
-     *     "derive" /
-     *     "traitalias")
+     * The supported syntax by this parser is given in the rustdoc book chapter
+     * /src/doc/rustdoc/src/read-documentation/search.md
      *
-     * OPEN-ANGLE-BRACKET = "<"
-     * CLOSE-ANGLE-BRACKET = ">"
-     * OPEN-SQUARE-BRACKET = "["
-     * CLOSE-SQUARE-BRACKET = "]"
-     * COLON = ":"
-     * DOUBLE-COLON = "::"
-     * QUOTE = %x22
-     * COMMA = ","
-     * RETURN-ARROW = "->"
-     *
-     * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
-     * DIGIT = %x30-39
-     * WS = %x09 / " "
+     * When adding new things to the parser, add them there, too!
      *
      * @param  {string} val     - The user query
      *
@@ -1124,6 +1087,7 @@ function initSearch(rawSearchIndex) {
             query.literalSearch = parserState.totalElems > 1;
         }
         query.foundElems = query.elems.length + query.returned.length;
+        query.totalElems = parserState.totalElems;
         return query;
     }
 
@@ -1172,7 +1136,7 @@ function initSearch(rawSearchIndex) {
             const out = [];
 
             for (const result of results) {
-                if (result.id > -1) {
+                if (result.id !== -1) {
                     const obj = searchIndex[result.id];
                     obj.dist = result.dist;
                     const res = buildHrefAndPath(obj);
@@ -1348,192 +1312,311 @@ function initSearch(rawSearchIndex) {
          * This function checks generics in search query `queryElem` can all be found in the
          * search index (`fnType`),
          *
-         * @param {FunctionType} fnType     - The object to check.
-         * @param {QueryElement} queryElem  - The element from the parsed query.
+         * This function returns `true` if it matches, and also writes the results to mgensInout.
+         * It returns `false` if no match is found, and leaves mgensInout untouched.
+         *
+         * @param {FunctionType} fnType                - The object to check.
+         * @param {QueryElement} queryElem             - The element from the parsed query.
+         * @param {[FunctionType]} whereClause         - Trait bounds for generic items.
+         * @param {Map<number,number>|null} mgensInout - Map functions generics to query generics.
          *
          * @return {boolean} - Returns true if a match, false otherwise.
          */
-        function checkGenerics(fnType, queryElem) {
-            return unifyFunctionTypes(fnType.generics, queryElem.generics);
+        function checkGenerics(fnType, queryElem, whereClause, mgensInout) {
+            return unifyFunctionTypes(
+                fnType.generics,
+                queryElem.generics,
+                whereClause,
+                mgensInout,
+                mgens => {
+                    if (mgensInout) {
+                        for (const [fid, qid] of mgens.entries()) {
+                            mgensInout.set(fid, qid);
+                        }
+                    }
+                    return true;
+                }
+            );
         }
         /**
          * This function checks if a list of search query `queryElems` can all be found in the
          * search index (`fnTypes`).
          *
-         * @param {Array<FunctionType>} fnTypes    - The objects to check.
+         * This function returns `true` on a match, or `false` if none. If `solutionCb` is
+         * supplied, it will call that function with mgens, and that callback can accept or
+         * reject the result bu returning `true` or `false`. If the callback returns false,
+         * then this function will try with a different solution, or bail with false if it
+         * runs out of candidates.
+         *
+         * @param {Array<FunctionType>} fnTypes - The objects to check.
          * @param {Array<QueryElement>} queryElems - The elements from the parsed query.
+         * @param {[FunctionType]} whereClause - Trait bounds for generic items.
+         * @param {Map<number,number>|null} mgensIn
+         *     - Map functions generics to query generics (never modified).
+         * @param {null|Map<number,number> -> bool} solutionCb - Called for each `mgens` solution.
          *
          * @return {boolean} - Returns true if a match, false otherwise.
          */
-        function unifyFunctionTypes(fnTypes, queryElems) {
-            // This search engine implements order-agnostic unification. There
-            // should be no missing duplicates (generics have "bag semantics"),
-            // and the row is allowed to have extras.
+        function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) {
+            /**
+             * @type Map<integer, integer>
+             */
+            let mgens = new Map(mgensIn);
             if (queryElems.length === 0) {
-                return true;
+                return !solutionCb || solutionCb(mgens);
             }
-            if (!fnTypes || fnTypes.length === 0) {
+            if (!fnTypesIn || fnTypesIn.length === 0) {
                 return false;
             }
+            const ql = queryElems.length;
+            let fl = fnTypesIn.length;
             /**
-             * @type Map<integer, QueryElement[]>
+             * @type Array<FunctionType>
              */
-            const queryElemSet = new Map();
-            const addQueryElemToQueryElemSet = queryElem => {
-                let currentQueryElemList;
-                if (queryElemSet.has(queryElem.id)) {
-                    currentQueryElemList = queryElemSet.get(queryElem.id);
-                } else {
-                    currentQueryElemList = [];
-                    queryElemSet.set(queryElem.id, currentQueryElemList);
-                }
-                currentQueryElemList.push(queryElem);
-            };
-            for (const queryElem of queryElems) {
-                addQueryElemToQueryElemSet(queryElem);
-            }
+            let fnTypes = fnTypesIn.slice();
             /**
-             * @type Map<integer, FunctionType[]>
+             * loop works by building up a solution set in the working arrays
+             * fnTypes gets mutated in place to make this work, while queryElems
+             * is left alone
+             *
+             *                                  vvvvvvv `i` points here
+             * queryElems = [ good, good, good, unknown, unknown ],
+             * fnTypes    = [ good, good, good, unknown, unknown ],
+             *                ----------------  ^^^^^^^^^^^^^^^^ `j` iterates after `i`,
+             *                |                                   looking for candidates
+             *                everything before `i` is the
+             *                current working solution
+             *
+             * Everything in the current working solution is known to be a good
+             * match, but it might not be the match we wind up going with, because
+             * there might be more than one candidate match, and we need to try them all
+             * before giving up. So, to handle this, it backtracks on failure.
+             *
+             * @type Array<{
+             *     "fnTypesScratch": Array<FunctionType>,
+             *     "queryElemsOffset": integer,
+             *     "fnTypesOffset": integer
+             * }>
              */
-            const fnTypeSet = new Map();
-            const addFnTypeToFnTypeSet = fnType => {
-                // Pure generic, or an item that's not matched by any query elems.
-                // Try [unboxing] it.
-                //
-                // [unboxing]:
-                // http://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
-                const queryContainsArrayOrSliceElem = queryElemSet.has(typeNameIdOfArrayOrSlice);
-                if (fnType.id === -1 || !(
-                    queryElemSet.has(fnType.id) ||
-                    (fnType.id === typeNameIdOfSlice && queryContainsArrayOrSliceElem) ||
-                    (fnType.id === typeNameIdOfArray && queryContainsArrayOrSliceElem)
-                )) {
-                    for (const innerFnType of fnType.generics) {
-                        addFnTypeToFnTypeSet(innerFnType);
-                    }
-                    return;
-                }
-                let currentQueryElemList = queryElemSet.get(fnType.id) || [];
-                let matchIdx = currentQueryElemList.findIndex(queryElem => {
-                    return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
-                        checkGenerics(fnType, queryElem);
-                });
-                if (matchIdx === -1 &&
-                    (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray) &&
-                    queryContainsArrayOrSliceElem
-                ) {
-                    currentQueryElemList = queryElemSet.get(typeNameIdOfArrayOrSlice) || [];
-                    matchIdx = currentQueryElemList.findIndex(queryElem => {
-                        return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
-                            checkGenerics(fnType, queryElem);
-                    });
-                }
-                // None of the query elems match the function type.
-                // Try [unboxing] it.
-                if (matchIdx === -1) {
-                    for (const innerFnType of fnType.generics) {
-                        addFnTypeToFnTypeSet(innerFnType);
+            const backtracking = [];
+            let i = 0;
+            let j = 0;
+            const backtrack = () => {
+                while (backtracking.length !== 0) {
+                    // this session failed, but there are other possible solutions
+                    // to backtrack, reset to (a copy of) the old array, do the swap or unboxing
+                    const {
+                        fnTypesScratch,
+                        mgensScratch,
+                        queryElemsOffset,
+                        fnTypesOffset,
+                        unbox,
+                    } = backtracking.pop();
+                    mgens = new Map(mgensScratch);
+                    const fnType = fnTypesScratch[fnTypesOffset];
+                    const queryElem = queryElems[queryElemsOffset];
+                    if (unbox) {
+                        if (fnType.id < 0) {
+                            if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
+                                continue;
+                            }
+                            mgens.set(fnType.id, 0);
+                        }
+                        const generics = fnType.id < 0 ?
+                            whereClause[(-fnType.id) - 1] :
+                            fnType.generics;
+                        fnTypes = fnTypesScratch.toSpliced(fnTypesOffset, 1, ...generics);
+                        fl = fnTypes.length;
+                        // re-run the matching algorithm on this item
+                        i = queryElemsOffset - 1;
+                    } else {
+                        if (fnType.id < 0) {
+                            if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
+                                continue;
+                            }
+                            mgens.set(fnType.id, queryElem.id);
+                        }
+                        fnTypes = fnTypesScratch.slice();
+                        fl = fnTypes.length;
+                        const tmp = fnTypes[queryElemsOffset];
+                        fnTypes[queryElemsOffset] = fnTypes[fnTypesOffset];
+                        fnTypes[fnTypesOffset] = tmp;
+                        // this is known as a good match; go to the next one
+                        i = queryElemsOffset;
                     }
-                    return;
-                }
-                let currentFnTypeList;
-                if (fnTypeSet.has(fnType.id)) {
-                    currentFnTypeList = fnTypeSet.get(fnType.id);
-                } else {
-                    currentFnTypeList = [];
-                    fnTypeSet.set(fnType.id, currentFnTypeList);
-                }
-                currentFnTypeList.push(fnType);
-            };
-            for (const fnType of fnTypes) {
-                addFnTypeToFnTypeSet(fnType);
-            }
-            const doHandleQueryElemList = (currentFnTypeList, queryElemList) => {
-                if (queryElemList.length === 0) {
                     return true;
                 }
-                // Multiple items in one list might match multiple items in another.
-                // Since an item with fewer generics can match an item with more, we
-                // need to check all combinations for a potential match.
-                const queryElem = queryElemList.pop();
-                const l = currentFnTypeList.length;
-                for (let i = 0; i < l; i += 1) {
-                    const fnType = currentFnTypeList[i];
-                    if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
-                        continue;
-                    }
-                    const queryElemPathLength = queryElem.pathWithoutLast.length;
-                    // If the query element is a path (it contains `::`), we need to check if this
-                    // path is compatible with the target type.
-                    if (queryElemPathLength > 0) {
-                        const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
-                            fnType.path.split("::") : [];
-                        // If the path provided in the query element is longer than this type,
-                        // no need to check it since it won't match in any case.
-                        if (queryElemPathLength > fnTypePath.length) {
-                            continue;
+                return false;
+            };
+            for (i = 0; i !== ql; ++i) {
+                const queryElem = queryElems[i];
+                /**
+                 * list of potential function types that go with the current query element.
+                 * @type Array<integer>
+                 */
+                const matchCandidates = [];
+                let fnTypesScratch = null;
+                let mgensScratch = null;
+                // don't try anything before `i`, because they've already been
+                // paired off with the other query elements
+                for (j = i; j !== fl; ++j) {
+                    const fnType = fnTypes[j];
+                    if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) {
+                        if (!fnTypesScratch) {
+                            fnTypesScratch = fnTypes.slice();
                         }
-                        let i = 0;
-                        for (const path of fnTypePath) {
-                            if (path === queryElem.pathWithoutLast[i]) {
-                                i += 1;
-                                if (i >= queryElemPathLength) {
-                                    break;
-                                }
+                        unifyFunctionTypes(
+                            fnType.generics,
+                            queryElem.generics,
+                            whereClause,
+                            mgens,
+                            mgensScratch => {
+                                matchCandidates.push({
+                                    fnTypesScratch,
+                                    mgensScratch,
+                                    queryElemsOffset: i,
+                                    fnTypesOffset: j,
+                                    unbox: false,
+                                });
+                                return false; // "reject" all candidates to gather all of them
                             }
+                        );
+                    }
+                    if (unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) {
+                        if (!fnTypesScratch) {
+                            fnTypesScratch = fnTypes.slice();
                         }
-                        if (i < queryElemPathLength) {
-                            // If we didn't find all parts of the path of the query element inside
-                            // the fn type, then it's not the right one.
-                            continue;
+                        if (!mgensScratch) {
+                            mgensScratch = new Map(mgens);
                         }
+                        backtracking.push({
+                            fnTypesScratch,
+                            mgensScratch,
+                            queryElemsOffset: i,
+                            fnTypesOffset: j,
+                            unbox: true,
+                        });
                     }
-                    if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) {
-                        currentFnTypeList.splice(i, 1);
-                        const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
-                        if (result) {
-                            return true;
-                        }
-                        currentFnTypeList.splice(i, 0, fnType);
+                }
+                if (matchCandidates.length === 0) {
+                    if (backtrack()) {
+                        continue;
+                    } else {
+                        return false;
                     }
                 }
+                // use the current candidate
+                const {fnTypesOffset: candidate, mgensScratch: mgensNew} = matchCandidates.pop();
+                if (fnTypes[candidate].id < 0 && queryElems[i].id < 0) {
+                    mgens.set(fnTypes[candidate].id, queryElems[i].id);
+                }
+                for (const [fid, qid] of mgensNew) {
+                    mgens.set(fid, qid);
+                }
+                // `i` and `j` are paired off
+                // `queryElems[i]` is left in place
+                // `fnTypes[j]` is swapped with `fnTypes[i]` to pair them off
+                const tmp = fnTypes[candidate];
+                fnTypes[candidate] = fnTypes[i];
+                fnTypes[i] = tmp;
+                // write other candidates to backtracking queue
+                for (const otherCandidate of matchCandidates) {
+                    backtracking.push(otherCandidate);
+                }
+                // If we're on the last item, check the solution with the callback
+                // backtrack if the callback says its unsuitable
+                while (i === (ql - 1) && solutionCb && !solutionCb(mgens)) {
+                    if (!backtrack()) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+        function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens) {
+            // type filters look like `trait:Read` or `enum:Result`
+            if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
                 return false;
-            };
-            const handleQueryElemList = (id, queryElemList) => {
-                if (!fnTypeSet.has(id)) {
-                    if (id === typeNameIdOfArrayOrSlice) {
-                        return handleQueryElemList(typeNameIdOfSlice, queryElemList) ||
-                            handleQueryElemList(typeNameIdOfArray, queryElemList);
+            }
+            // fnType.id < 0 means generic
+            // queryElem.id < 0 does too
+            // mgens[fnType.id] = queryElem.id
+            // or, if mgens[fnType.id] = 0, then we've matched this generic with a bare trait
+            // and should make that same decision everywhere it appears
+            if (fnType.id < 0 && queryElem.id < 0) {
+                if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
+                    return false;
+                }
+                for (const [fid, qid] of mgens.entries()) {
+                    if (fnType.id !== fid && queryElem.id === qid) {
+                        return false;
                     }
+                    if (fnType.id === fid && queryElem.id !== qid) {
+                        return false;
+                    }
+                }
+            } else if (fnType.id !== null) {
+                if (queryElem.id === typeNameIdOfArrayOrSlice &&
+                    (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray)
+                ) {
+                    // [] matches primitive:array or primitive:slice
+                    // if it matches, then we're fine, and this is an appropriate match candidate
+                } else if (fnType.id !== queryElem.id) {
                     return false;
                 }
-                const currentFnTypeList = fnTypeSet.get(id);
-                if (currentFnTypeList.length < queryElemList.length) {
-                    // It's not possible for all the query elems to find a match.
+                // If the query elem has generics, and the function doesn't,
+                // it can't match.
+                if (fnType.generics.length === 0 && queryElem.generics.length !== 0) {
                     return false;
                 }
-                const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
-                if (result) {
-                    // Found a solution.
-                    // Any items that weren't used for it can be unboxed, and might form
-                    // part of the solution for another item.
-                    for (const innerFnType of currentFnTypeList) {
-                        addFnTypeToFnTypeSet(innerFnType);
+                // If the query element is a path (it contains `::`), we need to check if this
+                // path is compatible with the target type.
+                const queryElemPathLength = queryElem.pathWithoutLast.length;
+                if (queryElemPathLength > 0) {
+                    const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
+                        fnType.path.split("::") : [];
+                    // If the path provided in the query element is longer than this type,
+                    // no need to check it since it won't match in any case.
+                    if (queryElemPathLength > fnTypePath.length) {
+                        return false;
                     }
-                    fnTypeSet.delete(id);
-                }
-                return result;
-            };
-            let queryElemSetSize = -1;
-            while (queryElemSetSize !== queryElemSet.size) {
-                queryElemSetSize = queryElemSet.size;
-                for (const [id, queryElemList] of queryElemSet) {
-                    if (handleQueryElemList(id, queryElemList)) {
-                        queryElemSet.delete(id);
+                    let i = 0;
+                    for (const path of fnTypePath) {
+                        if (path === queryElem.pathWithoutLast[i]) {
+                            i += 1;
+                            if (i >= queryElemPathLength) {
+                                break;
+                            }
+                        }
+                    }
+                    if (i < queryElemPathLength) {
+                        // If we didn't find all parts of the path of the query element inside
+                        // the fn type, then it's not the right one.
+                        return false;
                     }
                 }
             }
-            return queryElemSetSize === 0;
+            return true;
+        }
+        function unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens) {
+            if (fnType.id < 0 && queryElem.id >= 0) {
+                if (!whereClause) {
+                    return false;
+                }
+                // mgens[fnType.id] === 0 indicates that we committed to unboxing this generic
+                // mgens[fnType.id] === null indicates that we haven't decided yet
+                if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
+                    return false;
+                }
+                // This is only a potential unbox if the search query appears in the where clause
+                // for example, searching `Read -> usize` should find
+                // `fn read_all<R: Read>(R) -> Result<usize>`
+                // generic `R` is considered "unboxed"
+                return checkIfInList(whereClause[(-fnType.id) - 1], queryElem, whereClause);
+            } else if (fnType.generics && fnType.generics.length > 0) {
+                return checkIfInList(fnType.generics, queryElem, whereClause);
+            }
+            return false;
         }
 
         /**
@@ -1541,13 +1624,14 @@ function initSearch(rawSearchIndex) {
           * generics (if any).
           *
           * @param {Array<FunctionType>} list
-          * @param {QueryElement} elem    - The element from the parsed query.
+          * @param {QueryElement} elem          - The element from the parsed query.
+          * @param {[FunctionType]} whereClause - Trait bounds for generic items.
           *
           * @return {boolean} - Returns true if found, false otherwise.
           */
-        function checkIfInList(list, elem) {
+        function checkIfInList(list, elem, whereClause) {
             for (const entry of list) {
-                if (checkType(entry, elem)) {
+                if (checkType(entry, elem, whereClause)) {
                     return true;
                 }
             }
@@ -1559,14 +1643,26 @@ function initSearch(rawSearchIndex) {
           * generics (if any).
           *
           * @param {Row} row
-          * @param {QueryElement} elem      - The element from the parsed query.
+          * @param {QueryElement} elem          - The element from the parsed query.
+          * @param {[FunctionType]} whereClause - Trait bounds for generic items.
           *
           * @return {boolean} - Returns true if the type matches, false otherwise.
           */
-        function checkType(row, elem) {
-            if (row.id === -1) {
+        function checkType(row, elem, whereClause) {
+            if (row.id === null) {
                 // This is a pure "generic" search, no need to run other checks.
-                return row.generics.length > 0 ? checkIfInList(row.generics, elem) : false;
+                return row.generics.length > 0
+                    ? checkIfInList(row.generics, elem, whereClause)
+                    : false;
+            }
+
+            if (row.id < 0 && elem.id >= 0) {
+                const gid = (-row.id) - 1;
+                return checkIfInList(whereClause[gid], elem, whereClause);
+            }
+
+            if (row.id < 0 && elem.id < 0) {
+                return true;
             }
 
             const matchesExact = row.id === elem.id;
@@ -1576,7 +1672,7 @@ function initSearch(rawSearchIndex) {
             if ((matchesExact || matchesArrayOrSlice) &&
                 typePassesFilter(elem.typeFilter, row.ty)) {
                 if (elem.generics.length > 0) {
-                    return checkGenerics(row, elem);
+                    return checkGenerics(row, elem, whereClause, new Map());
                 }
                 return true;
             }
@@ -1584,7 +1680,7 @@ function initSearch(rawSearchIndex) {
             // If the current item does not match, try [unboxing] the generic.
             // [unboxing]:
             //   https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
-            return checkIfInList(row.generics, elem);
+            return checkIfInList(row.generics, elem, whereClause);
         }
 
         function checkPath(contains, ty, maxEditDistance) {
@@ -1785,13 +1881,15 @@ function initSearch(rawSearchIndex) {
             const fullId = row.id;
             const searchWord = searchWords[pos];
 
-            const in_args = row.type && row.type.inputs && checkIfInList(row.type.inputs, elem);
+            const in_args = row.type && row.type.inputs
+                && checkIfInList(row.type.inputs, elem, row.type.where_clause);
             if (in_args) {
                 // path_dist is 0 because no parent path information is currently stored
                 // in the search index
                 addIntoResults(results_in_args, fullId, pos, -1, 0, 0, maxEditDistance);
             }
-            const returned = row.type && row.type.output && checkIfInList(row.type.output, elem);
+            const returned = row.type && row.type.output
+                && checkIfInList(row.type.output, elem, row.type.where_clause);
             if (returned) {
                 addIntoResults(results_returned, fullId, pos, -1, 0, 0, maxEditDistance);
             }
@@ -1853,10 +1951,20 @@ function initSearch(rawSearchIndex) {
             }
 
             // If the result is too "bad", we return false and it ends this search.
-            if (!unifyFunctionTypes(row.type.inputs, parsedQuery.elems)) {
-                return;
-            }
-            if (!unifyFunctionTypes(row.type.output, parsedQuery.returned)) {
+            if (!unifyFunctionTypes(
+                row.type.inputs,
+                parsedQuery.elems,
+                row.type.where_clause,
+                null,
+                mgens => {
+                    return unifyFunctionTypes(
+                        row.type.output,
+                        parsedQuery.returned,
+                        row.type.where_clause,
+                        mgens
+                    );
+                }
+            )) {
                 return;
             }
 
@@ -1875,6 +1983,11 @@ function initSearch(rawSearchIndex) {
             }
             const maxEditDistance = Math.floor(queryLen / 3);
 
+            /**
+             * @type {Map<string, integer>}
+             */
+            const genericSymbols = new Map();
+
             /**
              * Convert names to ids in parsed query elements.
              * This is not used for the "In Names" tab, but is used for the
@@ -1891,7 +2004,7 @@ function initSearch(rawSearchIndex) {
                 if (typeNameIdMap.has(elem.pathLast)) {
                     elem.id = typeNameIdMap.get(elem.pathLast);
                 } else if (!parsedQuery.literalSearch) {
-                    let match = -1;
+                    let match = null;
                     let matchDist = maxEditDistance + 1;
                     let matchName = "";
                     for (const [name, id] of typeNameIdMap) {
@@ -1905,11 +2018,52 @@ function initSearch(rawSearchIndex) {
                             matchName = name;
                         }
                     }
-                    if (match !== -1) {
+                    if (match !== null) {
                         parsedQuery.correction = matchName;
                     }
                     elem.id = match;
                 }
+                if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1
+                     && elem.generics.length === 0)
+                    || elem.typeFilter === TY_GENERIC) {
+                    if (genericSymbols.has(elem.name)) {
+                        elem.id = genericSymbols.get(elem.name);
+                    } else {
+                        elem.id = -(genericSymbols.size + 1);
+                        genericSymbols.set(elem.name, elem.id);
+                    }
+                    if (elem.typeFilter === -1 && elem.name.length >= 3) {
+                        // Silly heuristic to catch if the user probably meant
+                        // to not write a generic parameter. We don't use it,
+                        // just bring it up.
+                        const maxPartDistance = Math.floor(elem.name.length / 3);
+                        let matchDist = maxPartDistance + 1;
+                        let matchName = "";
+                        for (const name of typeNameIdMap.keys()) {
+                            const dist = editDistance(name, elem.name, maxPartDistance);
+                            if (dist <= matchDist && dist <= maxPartDistance) {
+                                if (dist === matchDist && matchName > name) {
+                                    continue;
+                                }
+                                matchDist = dist;
+                                matchName = name;
+                            }
+                        }
+                        if (matchName !== "") {
+                            parsedQuery.proposeCorrectionFrom = elem.name;
+                            parsedQuery.proposeCorrectionTo = matchName;
+                        }
+                    }
+                    elem.typeFilter = TY_GENERIC;
+                }
+                if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) {
+                    // Rust does not have HKT
+                    parsedQuery.error = [
+                        "Generic type parameter ",
+                        elem.name,
+                        " does not accept generic parameters",
+                    ];
+                }
                 for (const elem2 of elem.generics) {
                     convertNameToId(elem2);
                 }
@@ -1943,8 +2097,11 @@ function initSearch(rawSearchIndex) {
                     elem = parsedQuery.returned[0];
                     for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
                         row = searchIndex[i];
-                        in_returned = row.type &&
-                            unifyFunctionTypes(row.type.output, parsedQuery.returned);
+                        in_returned = row.type && unifyFunctionTypes(
+                            row.type.output,
+                            parsedQuery.returned,
+                            row.type.where_clause
+                        );
                         if (in_returned) {
                             addIntoResults(
                                 results_others,
@@ -2295,6 +2452,13 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                 "Showing results for closest type name " +
                 `"${results.query.correction}" instead.</h3>`;
         }
+        if (results.query.proposeCorrectionFrom !== null) {
+            const orig = results.query.proposeCorrectionFrom;
+            const targ = results.query.proposeCorrectionTo;
+            output += "<h3 class=\"search-corrections\">" +
+                `Type "${orig}" not found and used as generic parameter. ` +
+                `Consider searching for "${targ}" instead.</h3>`;
+        }
 
         const resultsElem = document.createElement("div");
         resultsElem.id = "results";
@@ -2396,37 +2560,54 @@ ${item.displayPath}<span class="${type}">${name}</span>\
      * @return {Array<FunctionSearchType>}
      */
     function buildItemSearchTypeAll(types, lowercasePaths) {
+        return types.map(type => buildItemSearchType(type, lowercasePaths));
+    }
+
+    /**
+     * Converts a single type.
+     *
+     * @param {RawFunctionType} type
+     */
+    function buildItemSearchType(type, lowercasePaths) {
         const PATH_INDEX_DATA = 0;
         const GENERICS_DATA = 1;
-        return types.map(type => {
-            let pathIndex, generics;
-            if (typeof type === "number") {
-                pathIndex = type;
-                generics = [];
-            } else {
-                pathIndex = type[PATH_INDEX_DATA];
-                generics = buildItemSearchTypeAll(
-                    type[GENERICS_DATA],
-                    lowercasePaths
-                );
-            }
+        let pathIndex, generics;
+        if (typeof type === "number") {
+            pathIndex = type;
+            generics = [];
+        } else {
+            pathIndex = type[PATH_INDEX_DATA];
+            generics = buildItemSearchTypeAll(
+                type[GENERICS_DATA],
+                lowercasePaths
+            );
+        }
+        if (pathIndex < 0) {
+            // types less than 0 are generic parameters
+            // the actual names of generic parameters aren't stored, since they aren't API
+            return {
+                id: pathIndex,
+                ty: TY_GENERIC,
+                path: null,
+                generics,
+            };
+        }
+        if (pathIndex === 0) {
             // `0` is used as a sentinel because it's fewer bytes than `null`
-            if (pathIndex === 0) {
-                return {
-                    id: -1,
-                    ty: null,
-                    path: null,
-                    generics: generics,
-                };
-            }
-            const item = lowercasePaths[pathIndex - 1];
             return {
-                id: buildTypeMapIndex(item.name),
-                ty: item.ty,
-                path: item.path,
-                generics: generics,
+                id: null,
+                ty: null,
+                path: null,
+                generics,
             };
-        });
+        }
+        const item = lowercasePaths[pathIndex - 1];
+        return {
+            id: buildTypeMapIndex(item.name),
+            ty: item.ty,
+            path: item.path,
+            generics,
+        };
     }
 
     /**
@@ -2454,23 +2635,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         }
         let inputs, output;
         if (typeof functionSearchType[INPUTS_DATA] === "number") {
-            const pathIndex = functionSearchType[INPUTS_DATA];
-            if (pathIndex === 0) {
-                inputs = [{
-                    id: -1,
-                    ty: null,
-                    path: null,
-                    generics: [],
-                }];
-            } else {
-                const item = lowercasePaths[pathIndex - 1];
-                inputs = [{
-                    id: buildTypeMapIndex(item.name),
-                    ty: item.ty,
-                    path: item.path,
-                    generics: [],
-                }];
-            }
+            inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)];
         } else {
             inputs = buildItemSearchTypeAll(
                 functionSearchType[INPUTS_DATA],
@@ -2479,23 +2644,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         }
         if (functionSearchType.length > 1) {
             if (typeof functionSearchType[OUTPUT_DATA] === "number") {
-                const pathIndex = functionSearchType[OUTPUT_DATA];
-                if (pathIndex === 0) {
-                    output = [{
-                        id: -1,
-                        ty: null,
-                        path: null,
-                        generics: [],
-                    }];
-                } else {
-                    const item = lowercasePaths[pathIndex - 1];
-                    output = [{
-                        id: buildTypeMapIndex(item.name),
-                        ty: item.ty,
-                        path: item.path,
-                        generics: [],
-                    }];
-                }
+                output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)];
             } else {
                 output = buildItemSearchTypeAll(
                     functionSearchType[OUTPUT_DATA],
@@ -2505,8 +2654,15 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         } else {
             output = [];
         }
+        const where_clause = [];
+        const l = functionSearchType.length;
+        for (let i = 2; i < l; ++i) {
+            where_clause.push(typeof functionSearchType[i] === "number"
+                ? [buildItemSearchType(functionSearchType[i], lowercasePaths)]
+                : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths));
+        }
         return {
-            inputs, output,
+            inputs, output, where_clause,
         };
     }
 
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index 416517d15f5db..c7e6dd3615e94 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -23,7 +23,9 @@ function contentToDiffLine(key, value) {
 }
 
 function shouldIgnoreField(fieldName) {
-    return fieldName === "query" || fieldName === "correction";
+    return fieldName === "query" || fieldName === "correction" ||
+        fieldName === "proposeCorrectionFrom" ||
+        fieldName === "proposeCorrectionTo";
 }
 
 // This function is only called when no matching result was found and therefore will only display
diff --git a/tests/mir-opt/dont_inline_type_id.call.Inline.diff b/tests/mir-opt/dont_inline_type_id.call.Inline.diff
new file mode 100644
index 0000000000000..80cfe07b0cbf6
--- /dev/null
+++ b/tests/mir-opt/dont_inline_type_id.call.Inline.diff
@@ -0,0 +1,20 @@
+- // MIR for `call` before Inline
++ // MIR for `call` after Inline
+  
+  fn call(_1: &T) -> TypeId {
+      debug s => _1;
+      let mut _0: std::any::TypeId;
+      let mut _2: &T;
+  
+      bb0: {
+          StorageLive(_2);
+          _2 = &(*_1);
+          _0 = <T as Any>::type_id(move _2) -> [return: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dont_inline_type_id.rs b/tests/mir-opt/dont_inline_type_id.rs
new file mode 100644
index 0000000000000..d8a5663609443
--- /dev/null
+++ b/tests/mir-opt/dont_inline_type_id.rs
@@ -0,0 +1,15 @@
+// unit-test: Inline
+// compile-flags: --crate-type=lib -C panic=abort
+
+use std::any::Any;
+use std::any::TypeId;
+
+struct A<T: ?Sized + 'static> {
+    a: i32,
+    b: T,
+}
+
+// EMIT_MIR dont_inline_type_id.call.Inline.diff
+pub fn call<T: ?Sized + 'static>(s: &T) -> TypeId {
+    s.type_id()
+}
diff --git a/tests/mir-opt/inline_generically_if_sized.call.Inline.diff b/tests/mir-opt/inline_generically_if_sized.call.Inline.diff
new file mode 100644
index 0000000000000..0cf4565dc994c
--- /dev/null
+++ b/tests/mir-opt/inline_generically_if_sized.call.Inline.diff
@@ -0,0 +1,24 @@
+- // MIR for `call` before Inline
++ // MIR for `call` after Inline
+  
+  fn call(_1: &T) -> i32 {
+      debug s => _1;
+      let mut _0: i32;
+      let mut _2: &T;
++     scope 1 (inlined <T as Foo>::bar) {
++         debug self => _2;
++     }
+  
+      bb0: {
+          StorageLive(_2);
+          _2 = &(*_1);
+-         _0 = <T as Foo>::bar(move _2) -> [return: bb1, unwind unreachable];
+-     }
+- 
+-     bb1: {
++         _0 = const 0_i32;
+          StorageDead(_2);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/inline_generically_if_sized.rs b/tests/mir-opt/inline_generically_if_sized.rs
new file mode 100644
index 0000000000000..1acfff7a56b7c
--- /dev/null
+++ b/tests/mir-opt/inline_generically_if_sized.rs
@@ -0,0 +1,17 @@
+// unit-test: Inline
+// compile-flags: --crate-type=lib -C panic=abort
+
+trait Foo {
+    fn bar(&self) -> i32;
+}
+
+impl<T> Foo for T {
+    fn bar(&self) -> i32 {
+        0
+    }
+}
+
+// EMIT_MIR inline_generically_if_sized.call.Inline.diff
+pub fn call<T>(s: &T) -> i32 {
+    s.bar()
+}
diff --git a/tests/rustdoc-gui/search-corrections.goml b/tests/rustdoc-gui/search-corrections.goml
index 5d1b83b35c5ee..aeb3c9b31a3fd 100644
--- a/tests/rustdoc-gui/search-corrections.goml
+++ b/tests/rustdoc-gui/search-corrections.goml
@@ -54,3 +54,53 @@ assert-text: (
     ".search-corrections",
     "Type \"notablestructwithlongnamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
 )
+
+// Now, generic correction
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// Intentionally wrong spelling of "NotableStructWithLongName"
+write: (".search-input", "NotableStructWithLongNamr, NotableStructWithLongNamr")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+
+assert-css: (".search-corrections", {
+    "display": "block"
+})
+assert-text: (
+    ".search-corrections",
+    "Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
+)
+
+// Now, generic correction plus error
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// Intentionally wrong spelling of "NotableStructWithLongName"
+write: (".search-input", "Foo<NotableStructWithLongNamr>,y")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+
+assert-css: (".search-corrections", {
+    "display": "block"
+})
+assert-text: (
+    ".search-corrections",
+    "Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
+)
+
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// Intentionally wrong spelling of "NotableStructWithLongName"
+write: (".search-input", "generic:NotableStructWithLongNamr<x>,y")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+
+assert-css: (".error", {
+    "display": "block"
+})
+assert-text: (
+    ".error",
+    "Query parser error: \"Generic type parameter notablestructwithlongnamr does not accept generic parameters\"."
+)
diff --git a/tests/rustdoc-js-std/option-type-signatures.js b/tests/rustdoc-js-std/option-type-signatures.js
index 2599785066145..e154fa707ab3d 100644
--- a/tests/rustdoc-js-std/option-type-signatures.js
+++ b/tests/rustdoc-js-std/option-type-signatures.js
@@ -1,3 +1,7 @@
+// ignore-order
+
+const FILTER_CRATE = "std";
+
 const EXPECTED = [
     {
         'query': 'option, fnonce -> option',
@@ -19,4 +23,62 @@ const EXPECTED = [
             { 'path': 'std::option::Option', 'name': 'as_mut_slice' },
         ],
     },
+    {
+        'query': 'option<t>, option<t> -> option<t>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'or' },
+            { 'path': 'std::option::Option', 'name': 'xor' },
+        ],
+    },
+    {
+        'query': 'option<t>, option<u> -> option<u>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'and' },
+            { 'path': 'std::option::Option', 'name': 'zip' },
+        ],
+    },
+    {
+        'query': 'option<t>, option<u> -> option<t>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'and' },
+            { 'path': 'std::option::Option', 'name': 'zip' },
+        ],
+    },
+    {
+        'query': 'option<t>, option<u> -> option<t, u>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'zip' },
+        ],
+    },
+    {
+        'query': 'option<t>, e -> result<t, e>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'ok_or' },
+            { 'path': 'std::result::Result', 'name': 'transpose' },
+        ],
+    },
+    {
+        'query': 'result<option<t>, e> -> option<result<t, e>>',
+        'others': [
+            { 'path': 'std::result::Result', 'name': 'transpose' },
+        ],
+    },
+    {
+        'query': 'option<t>, option<t> -> bool',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'eq' },
+        ],
+    },
+    {
+        'query': 'option<option<t>> -> option<t>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'flatten' },
+        ],
+    },
+    {
+        'query': 'option<t>',
+        'returned': [
+            { 'path': 'std::result::Result', 'name': 'ok' },
+        ],
+    },
 ];
diff --git a/tests/rustdoc-js-std/vec-type-signatures.js b/tests/rustdoc-js-std/vec-type-signatures.js
new file mode 100644
index 0000000000000..18cf9d6efd0fd
--- /dev/null
+++ b/tests/rustdoc-js-std/vec-type-signatures.js
@@ -0,0 +1,22 @@
+// ignore-order
+
+const FILTER_CRATE = "std";
+
+const EXPECTED = [
+    {
+        'query': 'vec::intoiter<T> -> [T]',
+        'others': [
+            { 'path': 'std::vec::IntoIter', 'name': 'as_slice' },
+            { 'path': 'std::vec::IntoIter', 'name': 'as_mut_slice' },
+            { 'path': 'std::vec::IntoIter', 'name': 'next_chunk' },
+        ],
+    },
+    {
+        'query': 'vec::intoiter<T> -> []',
+        'others': [
+            { 'path': 'std::vec::IntoIter', 'name': 'as_slice' },
+            { 'path': 'std::vec::IntoIter', 'name': 'as_mut_slice' },
+            { 'path': 'std::vec::IntoIter', 'name': 'next_chunk' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/generics-match-ambiguity.js b/tests/rustdoc-js/generics-match-ambiguity.js
index a9932a16ca38b..edce4268c5ac7 100644
--- a/tests/rustdoc-js/generics-match-ambiguity.js
+++ b/tests/rustdoc-js/generics-match-ambiguity.js
@@ -79,6 +79,7 @@ const EXPECTED = [
             { 'path': 'generics_match_ambiguity', 'name': 'baac' },
             { 'path': 'generics_match_ambiguity', 'name': 'baaf' },
             { 'path': 'generics_match_ambiguity', 'name': 'baag' },
+            { 'path': 'generics_match_ambiguity', 'name': 'baah' },
         ],
     },
     {
diff --git a/tests/rustdoc-js/generics-trait.js b/tests/rustdoc-js/generics-trait.js
index 4ccfb8f4e4d02..a71393b5e0502 100644
--- a/tests/rustdoc-js/generics-trait.js
+++ b/tests/rustdoc-js/generics-trait.js
@@ -12,11 +12,15 @@ const EXPECTED = [
         ],
     },
     {
-        'query': 'Result<SomeTraiz>',
-        'correction': null,
+        'query': 'Resulx<SomeTrait>',
         'in_args': [],
         'returned': [],
     },
+    {
+        'query': 'Result<SomeTraiz>',
+        'proposeCorrectionFrom': 'SomeTraiz',
+        'proposeCorrectionTo': 'SomeTrait',
+    },
     {
         'query': 'OtherThingxxxxxxxx',
         'correction': null,
diff --git a/tests/rustdoc-js/generics-unbox.js b/tests/rustdoc-js/generics-unbox.js
new file mode 100644
index 0000000000000..9cdfc7ac8b6e5
--- /dev/null
+++ b/tests/rustdoc-js/generics-unbox.js
@@ -0,0 +1,38 @@
+// exact-check
+
+const EXPECTED = [
+    {
+        'query': 'Inside<T> -> Out1<T>',
+        'others': [
+            { 'path': 'generics_unbox', 'name': 'alpha' },
+        ],
+    },
+    {
+        'query': 'Inside<T> -> Out3<T>',
+        'others': [
+            { 'path': 'generics_unbox', 'name': 'beta' },
+            { 'path': 'generics_unbox', 'name': 'gamma' },
+        ],
+    },
+    {
+        'query': 'Inside<T> -> Out4<T>',
+        'others': [
+            { 'path': 'generics_unbox', 'name': 'beta' },
+            { 'path': 'generics_unbox', 'name': 'gamma' },
+        ],
+    },
+    {
+        'query': 'Inside<T> -> Out3<U, T>',
+        'others': [
+            { 'path': 'generics_unbox', 'name': 'beta' },
+            { 'path': 'generics_unbox', 'name': 'gamma' },
+        ],
+    },
+    {
+        'query': 'Inside<T> -> Out4<U, T>',
+        'others': [
+            { 'path': 'generics_unbox', 'name': 'beta' },
+            { 'path': 'generics_unbox', 'name': 'gamma' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/generics-unbox.rs b/tests/rustdoc-js/generics-unbox.rs
new file mode 100644
index 0000000000000..bef34f891e95c
--- /dev/null
+++ b/tests/rustdoc-js/generics-unbox.rs
@@ -0,0 +1,36 @@
+pub struct Out<A, B = ()> {
+    a: A,
+    b: B,
+}
+
+pub struct Out1<A, const N: usize> {
+    a: [A; N],
+}
+
+pub struct Out2<A, const N: usize> {
+    a: [A; N],
+}
+
+pub struct Out3<A, B> {
+    a: A,
+    b: B,
+}
+
+pub struct Out4<A, B> {
+    a: A,
+    b: B,
+}
+
+pub struct Inside<T>(T);
+
+pub fn alpha<const N: usize, T>(_: Inside<T>) -> Out<Out1<T, N>, Out2<T, N>> {
+    loop {}
+}
+
+pub fn beta<T, U>(_: Inside<T>) -> Out<Out3<T, U>, Out4<U, T>> {
+    loop {}
+}
+
+pub fn gamma<T, U>(_: Inside<T>) -> Out<Out3<U, T>, Out4<T, U>> {
+    loop {}
+}
diff --git a/tests/rustdoc-js/type-parameters.js b/tests/rustdoc-js/type-parameters.js
new file mode 100644
index 0000000000000..e695f189bb672
--- /dev/null
+++ b/tests/rustdoc-js/type-parameters.js
@@ -0,0 +1,87 @@
+// exact-check
+// ignore-order
+
+const EXPECTED = [
+    {
+        query: '-> trait:Some',
+        others: [
+            { path: 'foo', name: 'alef' },
+            { path: 'foo', name: 'alpha' },
+        ],
+    },
+    {
+        query: '-> generic:T',
+        others: [
+            { path: 'foo', name: 'bet' },
+            { path: 'foo', name: 'alef' },
+            { path: 'foo', name: 'beta' },
+        ],
+    },
+    {
+        query: 'A -> B',
+        others: [
+            { path: 'foo', name: 'bet' },
+        ],
+    },
+    {
+        query: 'A -> A',
+        others: [
+            { path: 'foo', name: 'beta' },
+        ],
+    },
+    {
+        query: 'A, A',
+        others: [
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'A, B',
+        others: [
+            { path: 'foo', name: 'other' },
+        ],
+    },
+    {
+        query: 'Other, Other',
+        others: [
+            { path: 'foo', name: 'other' },
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'generic:T',
+        in_args: [
+            { path: 'foo', name: 'bet' },
+            { path: 'foo', name: 'beta' },
+            { path: 'foo', name: 'other' },
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'generic:Other',
+        in_args: [
+            { path: 'foo', name: 'bet' },
+            { path: 'foo', name: 'beta' },
+            { path: 'foo', name: 'other' },
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'trait:Other',
+        in_args: [
+            { path: 'foo', name: 'other' },
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'Other',
+        in_args: [
+            { path: 'foo', name: 'other' },
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'trait:T',
+        in_args: [],
+    },
+];
diff --git a/tests/rustdoc-js/type-parameters.rs b/tests/rustdoc-js/type-parameters.rs
new file mode 100644
index 0000000000000..cda5e26171fca
--- /dev/null
+++ b/tests/rustdoc-js/type-parameters.rs
@@ -0,0 +1,15 @@
+#![crate_name="foo"]
+
+pub trait Some {}
+impl Some for () {}
+pub trait Other {}
+impl Other for () {}
+
+pub fn alef<T: Some>() -> T { loop {} }
+pub fn alpha() -> impl Some { }
+
+pub fn bet<T, U>(t: T) -> U { loop {} }
+pub fn beta<T>(t: T) -> T {}
+
+pub fn other<T: Other, U: Other>(t: T, u: U) { loop {} }
+pub fn alternate<T: Other>(t: T, u: T) { loop {} }
diff --git a/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr
index 3b63ec45804ea..8f45902035e92 100644
--- a/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr
+++ b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr
@@ -16,6 +16,14 @@ LL |     build(Bar);
    |     required by a bound introduced by this call
    |
    = help: the trait `for<'a> Send` is not implemented for `impl Future<Output = ()> { <_ as Foo>::bar() }`
+note: this is a known limitation of the trait solver that will be lifted in the future
+  --> $DIR/normalizing-self-auto-trait-issue-109924.rs:23:11
+   |
+LL |     build(Bar);
+   |     ------^^^-
+   |     |     |
+   |     |     the trait solver is unable to infer the generic types that should be inferred from this argument
+   |     add turbofish arguments to this call to specify the types manually, even if it's redundant
 note: required by a bound in `build`
   --> $DIR/normalizing-self-auto-trait-issue-109924.rs:20:39
    |
diff --git a/tests/ui/const_prop/dont-propagate-generic-instance-2.rs b/tests/ui/const_prop/dont-propagate-generic-instance-2.rs
new file mode 100644
index 0000000000000..e5525af23f0e6
--- /dev/null
+++ b/tests/ui/const_prop/dont-propagate-generic-instance-2.rs
@@ -0,0 +1,26 @@
+// run-pass
+
+#![feature(inline_const)]
+
+// Makes sure we don't propagate generic instances of `Self: ?Sized` blanket impls.
+// This is relevant when we have an overlapping impl and builtin dyn instance.
+// See <https://github.com/rust-lang/rust/pull/114941> for more context.
+
+trait Trait {
+    fn foo(&self) -> &'static str;
+}
+
+impl<T: ?Sized> Trait for T {
+    fn foo(&self) -> &'static str {
+        std::any::type_name::<T>()
+    }
+}
+
+fn bar<T: ?Sized>() -> fn(&T) -> &'static str {
+    const { Trait::foo as fn(&T) -> &'static str }
+    // If const prop were to propagate the instance
+}
+
+fn main() {
+    assert_eq!("i32", bar::<dyn Trait>()(&1i32));
+}
diff --git a/tests/ui/const_prop/dont-propagate-generic-instance.rs b/tests/ui/const_prop/dont-propagate-generic-instance.rs
new file mode 100644
index 0000000000000..5994961b887b1
--- /dev/null
+++ b/tests/ui/const_prop/dont-propagate-generic-instance.rs
@@ -0,0 +1,24 @@
+// run-pass
+
+// Makes sure we don't propagate generic instances of `Self: ?Sized` blanket impls.
+// This is relevant when we have an overlapping impl and builtin dyn instance.
+// See <https://github.com/rust-lang/rust/pull/114941> for more context.
+
+trait Trait {
+    fn foo(&self) -> &'static str;
+}
+
+impl<T: ?Sized> Trait for T {
+    fn foo(&self) -> &'static str {
+        std::any::type_name::<T>()
+    }
+}
+
+const fn bar<T: ?Sized>() -> fn(&T) -> &'static str {
+    Trait::foo
+    // If const prop were to propagate the instance
+}
+
+fn main() {
+    assert_eq!("i32", bar::<dyn Trait>()(&1i32));
+}
diff --git a/tests/ui/generic-associated-types/bugs/issue-88460.stderr b/tests/ui/generic-associated-types/bugs/issue-88460.stderr
index a2047f103d488..b9e2c4186c14a 100644
--- a/tests/ui/generic-associated-types/bugs/issue-88460.stderr
+++ b/tests/ui/generic-associated-types/bugs/issue-88460.stderr
@@ -7,6 +7,14 @@ LL |     test(Foo);
    |     required by a bound introduced by this call
    |
    = help: the trait `Marker` is implemented for `()`
+note: this is a known limitation of the trait solver that will be lifted in the future
+  --> $DIR/issue-88460.rs:28:10
+   |
+LL |     test(Foo);
+   |     -----^^^-
+   |     |    |
+   |     |    the trait solver is unable to infer the generic types that should be inferred from this argument
+   |     add turbofish arguments to this call to specify the types manually, even if it's redundant
 note: required by a bound in `test`
   --> $DIR/issue-88460.rs:15:27
    |
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr
index b30dd36d2ad6a..2cc2bb2bbc3c9 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr
@@ -8,6 +8,14 @@ LL |         call(f, ());
    |
    = note: expected a closure with arguments `((),)`
               found a closure with arguments `(<_ as ATC<'a>>::Type,)`
+note: this is a known limitation of the trait solver that will be lifted in the future
+  --> $DIR/issue-62529-3.rs:25:14
+   |
+LL |         call(f, ());
+   |         -----^-----
+   |         |    |
+   |         |    the trait solver is unable to infer the generic types that should be inferred from this argument
+   |         add turbofish arguments to this call to specify the types manually, even if it's redundant
 note: required by a bound in `call`
   --> $DIR/issue-62529-3.rs:9:36
    |
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr
index 5be33bccdc317..55eaef78634c0 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr
@@ -7,6 +7,14 @@ LL |     upcast(y)
    |     required by a bound introduced by this call
    |
    = help: the trait `IsCovariant<'a>` is implemented for `std::borrow::Cow<'a, T>`
+note: this is a known limitation of the trait solver that will be lifted in the future
+  --> $DIR/issue-90950.rs:50:12
+   |
+LL |     upcast(y)
+   |     -------^-
+   |     |      |
+   |     |      the trait solver is unable to infer the generic types that should be inferred from this argument
+   |     add turbofish arguments to this call to specify the types manually, even if it's redundant
 note: required by a bound in `upcast`
   --> $DIR/issue-90950.rs:27:42
    |
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr
index 73388a72574e2..081dbb67df85a 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `for<'a> <_ as Trait<'a>>::Out: Copy` is not satis
 LL |     let _: () = weird_bound();
    |                 ^^^^^^^^^^^ the trait `for<'a> Copy` is not implemented for `<_ as Trait<'a>>::Out`
    |
+note: this is a known limitation of the trait solver that will be lifted in the future
+  --> $DIR/norm-before-method-resolution.rs:22:17
+   |
+LL |     let _: () = weird_bound();
+   |                 ^^^^^^^^^^^ try adding turbofish arguments to this expression to specify the types manually, even if it's redundant
 note: required by a bound in `weird_bound`
   --> $DIR/norm-before-method-resolution.rs:18:40
    |
diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.rs b/tests/ui/repr/repr-transparent-non-exhaustive.rs
index 506f1dcf3fc51..84dd3f49239f2 100644
--- a/tests/ui/repr/repr-transparent-non-exhaustive.rs
+++ b/tests/ui/repr/repr-transparent-non-exhaustive.rs
@@ -93,4 +93,44 @@ pub struct T16(Sized, ExternalIndirection<NonExhaustiveVariant>);
 //~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
 //~| WARN this was previously accepted by the compiler
 
+#[repr(transparent)]
+pub struct T17(NonExhaustive, Sized);
+//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T18(NonExhaustive, NonExhaustive);
+//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T19(NonExhaustive, Private);
+//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T19Flipped(Private, NonExhaustive);
+//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T20(NonExhaustive);
+// Okay, since it's the only field.
+
+#[repr(transparent)]
+pub struct T21(NonExhaustive, InternalNonExhaustive);
+// Okay, since there's only 1 foreign non-exhaustive type.
+
+#[repr(transparent)]
+pub struct T21Flipped(InternalNonExhaustive, NonExhaustive);
+// Okay, since there's only 1 foreign non-exhaustive type.
+
+#[repr(transparent)]
+pub struct T22(NonExhaustive, InternalPrivate);
+// Okay, since there's only 1 foreign non-exhaustive type.
+
+#[repr(transparent)]
+pub struct T22Flipped(InternalPrivate, NonExhaustive);
+// Okay, since there's only 1 foreign non-exhaustive type.
+
 fn main() {}
diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.stderr b/tests/ui/repr/repr-transparent-non-exhaustive.stderr
index 16edf59c7cc34..808b9bc986d91 100644
--- a/tests/ui/repr/repr-transparent-non-exhaustive.stderr
+++ b/tests/ui/repr/repr-transparent-non-exhaustive.stderr
@@ -123,5 +123,45 @@ LL | pub struct T16(Sized, ExternalIndirection<NonExhaustiveVariant>);
    = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
    = note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
 
-error: aborting due to 12 previous errors
+error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:97:16
+   |
+LL | pub struct T17(NonExhaustive, Sized);
+   |                ^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:102:31
+   |
+LL | pub struct T18(NonExhaustive, NonExhaustive);
+   |                               ^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:107:31
+   |
+LL | pub struct T19(NonExhaustive, Private);
+   |                               ^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:112:32
+   |
+LL | pub struct T19Flipped(Private, NonExhaustive);
+   |                                ^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: aborting due to 16 previous errors