about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/bootstrap/toolstate.rs2
-rwxr-xr-xsrc/ci/docker/x86_64-gnu-tools/checktools.sh1
-rw-r--r--src/librustc_ast_passes/ast_validation.rs96
-rw-r--r--src/librustc_hir/hir.rs36
-rw-r--r--src/librustc_middle/mir/interpret/error.rs19
-rw-r--r--src/librustc_middle/ty/diagnostics.rs184
-rw-r--r--src/librustc_middle/ty/error.rs43
-rw-r--r--src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs3
-rw-r--r--src/librustc_mir/interpret/operand.rs3
-rw-r--r--src/librustc_mir/interpret/traits.rs9
-rw-r--r--src/librustc_mir/interpret/validity.rs288
-rw-r--r--src/librustc_trait_selection/traits/error_reporting/mod.rs186
-rw-r--r--src/librustc_trait_selection/traits/error_reporting/suggestions.rs4
-rw-r--r--src/librustc_typeck/check/method/suggest.rs104
-rw-r--r--src/librustc_typeck/check/op.rs92
-rw-r--r--src/librustdoc/html/markdown.rs31
-rw-r--r--src/librustdoc/html/markdown/tests.rs8
-rw-r--r--src/librustdoc/html/render.rs22
-rw-r--r--src/librustdoc/html/static/main.js2
-rw-r--r--src/test/rustdoc/const-generics/add-impl.rs2
-rw-r--r--src/test/rustdoc/duplicate_impls/issue-33054.rs2
-rw-r--r--src/test/rustdoc/issue-21474.rs2
-rw-r--r--src/test/rustdoc/issue-45584.rs4
-rw-r--r--src/test/rustdoc/issue-55321.rs4
-rw-r--r--src/test/rustdoc/negative-impl-sidebar.rs2
-rw-r--r--src/test/rustdoc/struct-implementations-title.rs9
-rw-r--r--src/test/rustdoc/synthetic_auto/manual.rs4
-rw-r--r--src/test/rustdoc/typedef.rs4
-rw-r--r--src/test/ui/consts/const-eval/ub-wide-ptr.rs8
-rw-r--r--src/test/ui/consts/const-eval/ub-wide-ptr.stderr52
-rw-r--r--src/test/ui/generic-associated-types/construct_with_other_type.stderr9
-rw-r--r--src/test/ui/generic-associated-types/missing-bounds.fixed46
-rw-r--r--src/test/ui/generic-associated-types/missing-bounds.rs46
-rw-r--r--src/test/ui/generic-associated-types/missing-bounds.stderr77
-rw-r--r--src/test/ui/issues/issue-20005.stderr12
-rw-r--r--src/test/ui/issues/issue-24204.stderr4
-rw-r--r--src/test/ui/issues/issue-6738.stderr5
-rw-r--r--src/test/ui/type/type-check/missing_trait_impl.stderr10
38 files changed, 950 insertions, 485 deletions
diff --git a/src/bootstrap/toolstate.rs b/src/bootstrap/toolstate.rs
index cd8ce4881b1..6c219cee01e 100644
--- a/src/bootstrap/toolstate.rs
+++ b/src/bootstrap/toolstate.rs
@@ -88,7 +88,7 @@ static STABLE_TOOLS: &[(&str, &str)] = &[
 static NIGHTLY_TOOLS: &[(&str, &str)] = &[
     ("miri", "src/tools/miri"),
     ("embedded-book", "src/doc/embedded-book"),
-    ("rustc-dev-guide", "src/doc/rustc-dev-guide"),
+    // ("rustc-dev-guide", "src/doc/rustc-dev-guide"),
 ];
 
 fn print_error(tool: &str, submodule: &str) {
diff --git a/src/ci/docker/x86_64-gnu-tools/checktools.sh b/src/ci/docker/x86_64-gnu-tools/checktools.sh
index 00ac20c08dc..c9d1cb21da8 100755
--- a/src/ci/docker/x86_64-gnu-tools/checktools.sh
+++ b/src/ci/docker/x86_64-gnu-tools/checktools.sh
@@ -14,7 +14,6 @@ python3 "$X_PY" test --no-fail-fast \
     src/doc/rust-by-example \
     src/doc/embedded-book \
     src/doc/edition-guide \
-    src/doc/rustc-dev-guide \
     src/tools/clippy \
     src/tools/rls \
     src/tools/rustfmt \
diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs
index 395fd746085..cc88fbb295c 100644
--- a/src/librustc_ast_passes/ast_validation.rs
+++ b/src/librustc_ast_passes/ast_validation.rs
@@ -23,6 +23,7 @@ use rustc_session::Session;
 use rustc_span::symbol::{kw, sym};
 use rustc_span::Span;
 use std::mem;
+use std::ops::DerefMut;
 
 const MORE_EXTERN: &str =
     "for more information, visit https://doc.rust-lang.org/std/keyword.extern.html";
@@ -1113,17 +1114,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
         for predicate in &generics.where_clause.predicates {
             if let WherePredicate::EqPredicate(ref predicate) = *predicate {
-                self.err_handler()
-                    .struct_span_err(
-                        predicate.span,
-                        "equality constraints are not yet supported in `where` clauses",
-                    )
-                    .span_label(predicate.span, "not supported")
-                    .note(
-                        "see issue #20041 <https://github.com/rust-lang/rust/issues/20041> \
-                         for more information",
-                    )
-                    .emit();
+                deny_equality_constraints(self, predicate, generics);
             }
         }
 
@@ -1300,6 +1291,89 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
     }
 }
 
+/// When encountering an equality constraint in a `where` clause, emit an error. If the code seems
+/// like it's setting an associated type, provide an appropriate suggestion.
+fn deny_equality_constraints(
+    this: &mut AstValidator<'_>,
+    predicate: &WhereEqPredicate,
+    generics: &Generics,
+) {
+    let mut err = this.err_handler().struct_span_err(
+        predicate.span,
+        "equality constraints are not yet supported in `where` clauses",
+    );
+    err.span_label(predicate.span, "not supported");
+
+    // Given `<A as Foo>::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
+    if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind {
+        if let TyKind::Path(None, path) = &qself.ty.kind {
+            match &path.segments[..] {
+                [PathSegment { ident, args: None, .. }] => {
+                    for param in &generics.params {
+                        if param.ident == *ident {
+                            let param = ident;
+                            match &full_path.segments[qself.position..] {
+                                [PathSegment { ident, .. }] => {
+                                    // Make a new `Path` from `foo::Bar` to `Foo<Bar = RhsTy>`.
+                                    let mut assoc_path = full_path.clone();
+                                    // Remove `Bar` from `Foo::Bar`.
+                                    assoc_path.segments.pop();
+                                    let len = assoc_path.segments.len() - 1;
+                                    // Build `<Bar = RhsTy>`.
+                                    let arg = AngleBracketedArg::Constraint(AssocTyConstraint {
+                                        id: rustc_ast::node_id::DUMMY_NODE_ID,
+                                        ident: *ident,
+                                        kind: AssocTyConstraintKind::Equality {
+                                            ty: predicate.rhs_ty.clone(),
+                                        },
+                                        span: ident.span,
+                                    });
+                                    // Add `<Bar = RhsTy>` to `Foo`.
+                                    match &mut assoc_path.segments[len].args {
+                                        Some(args) => match args.deref_mut() {
+                                            GenericArgs::Parenthesized(_) => continue,
+                                            GenericArgs::AngleBracketed(args) => {
+                                                args.args.push(arg);
+                                            }
+                                        },
+                                        empty_args => {
+                                            *empty_args = AngleBracketedArgs {
+                                                span: ident.span,
+                                                args: vec![arg],
+                                            }
+                                            .into();
+                                        }
+                                    }
+                                    err.span_suggestion_verbose(
+                                        predicate.span,
+                                        &format!(
+                                            "if `{}` is an associated type you're trying to set, \
+                                            use the associated type binding syntax",
+                                            ident
+                                        ),
+                                        format!(
+                                            "{}: {}",
+                                            param,
+                                            pprust::path_to_string(&assoc_path)
+                                        ),
+                                        Applicability::MaybeIncorrect,
+                                    );
+                                }
+                                _ => {}
+                            };
+                        }
+                    }
+                }
+                _ => {}
+            }
+        }
+    }
+    err.note(
+        "see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information",
+    );
+    err.emit();
+}
+
 pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool {
     let mut validator = AstValidator {
         session,
diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs
index 258428d77da..e6d673b30f7 100644
--- a/src/librustc_hir/hir.rs
+++ b/src/librustc_hir/hir.rs
@@ -2626,8 +2626,42 @@ impl Node<'_> {
         match self {
             Node::TraitItem(TraitItem { generics, .. })
             | Node::ImplItem(ImplItem { generics, .. })
-            | Node::Item(Item { kind: ItemKind::Fn(_, generics, _), .. }) => Some(generics),
+            | Node::Item(Item {
+                kind:
+                    ItemKind::Trait(_, _, generics, ..)
+                    | ItemKind::Impl { generics, .. }
+                    | ItemKind::Fn(_, generics, _),
+                ..
+            }) => Some(generics),
             _ => None,
         }
     }
+
+    pub fn hir_id(&self) -> Option<HirId> {
+        match self {
+            Node::Item(Item { hir_id, .. })
+            | Node::ForeignItem(ForeignItem { hir_id, .. })
+            | Node::TraitItem(TraitItem { hir_id, .. })
+            | Node::ImplItem(ImplItem { hir_id, .. })
+            | Node::Field(StructField { hir_id, .. })
+            | Node::AnonConst(AnonConst { hir_id, .. })
+            | Node::Expr(Expr { hir_id, .. })
+            | Node::Stmt(Stmt { hir_id, .. })
+            | Node::Ty(Ty { hir_id, .. })
+            | Node::Binding(Pat { hir_id, .. })
+            | Node::Pat(Pat { hir_id, .. })
+            | Node::Arm(Arm { hir_id, .. })
+            | Node::Block(Block { hir_id, .. })
+            | Node::Local(Local { hir_id, .. })
+            | Node::MacroDef(MacroDef { hir_id, .. })
+            | Node::Lifetime(Lifetime { hir_id, .. })
+            | Node::Param(Param { hir_id, .. })
+            | Node::GenericParam(GenericParam { hir_id, .. }) => Some(*hir_id),
+            Node::TraitRef(TraitRef { hir_ref_id, .. }) => Some(*hir_ref_id),
+            Node::PathSegment(PathSegment { hir_id, .. }) => *hir_id,
+            Node::Variant(Variant { id, .. }) => Some(*id),
+            Node::Ctor(variant) => variant.ctor_hir_id(),
+            Node::Crate(_) | Node::Visibility(_) => None,
+        }
+    }
 }
diff --git a/src/librustc_middle/mir/interpret/error.rs b/src/librustc_middle/mir/interpret/error.rs
index 4b88467ac11..ffe71eb3a09 100644
--- a/src/librustc_middle/mir/interpret/error.rs
+++ b/src/librustc_middle/mir/interpret/error.rs
@@ -3,8 +3,7 @@ use super::{AllocId, Pointer, RawConst, ScalarMaybeUndef};
 use crate::mir::interpret::ConstValue;
 use crate::ty::layout::LayoutError;
 use crate::ty::query::TyCtxtAt;
-use crate::ty::tls;
-use crate::ty::{self, layout, Ty};
+use crate::ty::{self, layout, tls, FnSig, Ty};
 
 use rustc_data_structures::sync::Lock;
 use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorReported};
@@ -329,7 +328,7 @@ impl fmt::Display for CheckInAllocMsg {
 }
 
 /// Error information for when the program caused Undefined Behavior.
-pub enum UndefinedBehaviorInfo {
+pub enum UndefinedBehaviorInfo<'tcx> {
     /// Free-form case. Only for errors that are never caught!
     Ub(String),
     /// Unreachable code was executed.
@@ -347,6 +346,8 @@ pub enum UndefinedBehaviorInfo {
     PointerArithOverflow,
     /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
     InvalidMeta(&'static str),
+    /// Invalid drop function in vtable.
+    InvalidDropFn(FnSig<'tcx>),
     /// Reading a C string that does not end within its allocation.
     UnterminatedCString(Pointer),
     /// Dereferencing a dangling pointer after it got freed.
@@ -380,6 +381,8 @@ pub enum UndefinedBehaviorInfo {
     InvalidDiscriminant(ScalarMaybeUndef),
     /// Using a pointer-not-to-a-function as function pointer.
     InvalidFunctionPointer(Pointer),
+    /// Using a string that is not valid UTF-8,
+    InvalidStr(std::str::Utf8Error),
     /// Using uninitialized data where it is not allowed.
     InvalidUndefBytes(Option<Pointer>),
     /// Working with a local that is not currently live.
@@ -391,7 +394,7 @@ pub enum UndefinedBehaviorInfo {
     },
 }
 
-impl fmt::Display for UndefinedBehaviorInfo {
+impl fmt::Display for UndefinedBehaviorInfo<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         use UndefinedBehaviorInfo::*;
         match self {
@@ -404,6 +407,11 @@ impl fmt::Display for UndefinedBehaviorInfo {
             RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
             PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
             InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
+            InvalidDropFn(sig) => write!(
+                f,
+                "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
+                sig
+            ),
             UnterminatedCString(p) => write!(
                 f,
                 "reading a null-terminated string starting at {} with no null found before end of allocation",
@@ -446,6 +454,7 @@ impl fmt::Display for UndefinedBehaviorInfo {
             InvalidFunctionPointer(p) => {
                 write!(f, "using {} as function pointer but it does not point to a function", p)
             }
+            InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err),
             InvalidUndefBytes(Some(p)) => write!(
                 f,
                 "reading uninitialized memory at {}, but this operation requires initialized memory",
@@ -549,7 +558,7 @@ impl dyn MachineStopType {
 
 pub enum InterpError<'tcx> {
     /// The program caused undefined behavior.
-    UndefinedBehavior(UndefinedBehaviorInfo),
+    UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
     /// The program did something the interpreter does not support (some of these *might* be UB
     /// but the interpreter is not sure).
     Unsupported(UnsupportedOpInfo),
diff --git a/src/librustc_middle/ty/diagnostics.rs b/src/librustc_middle/ty/diagnostics.rs
index 790eb8f49af..613d66d59c5 100644
--- a/src/librustc_middle/ty/diagnostics.rs
+++ b/src/librustc_middle/ty/diagnostics.rs
@@ -2,7 +2,12 @@
 
 use crate::ty::sty::InferTy;
 use crate::ty::TyKind::*;
-use crate::ty::TyS;
+use crate::ty::{TyCtxt, TyS};
+use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
+use rustc_span::{BytePos, Span};
 
 impl<'tcx> TyS<'tcx> {
     /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
@@ -67,3 +72,180 @@ impl<'tcx> TyS<'tcx> {
         }
     }
 }
+
+/// Suggest restricting a type param with a new bound.
+pub fn suggest_constraining_type_param(
+    tcx: TyCtxt<'_>,
+    generics: &hir::Generics<'_>,
+    err: &mut DiagnosticBuilder<'_>,
+    param_name: &str,
+    constraint: &str,
+    def_id: Option<DefId>,
+) -> bool {
+    let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
+
+    let param = if let Some(param) = param {
+        param
+    } else {
+        return false;
+    };
+
+    const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
+    let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
+    let msg_restrict_type_further =
+        format!("consider further restricting type parameter `{}`", param_name);
+
+    if def_id == tcx.lang_items().sized_trait() {
+        // Type parameters are already `Sized` by default.
+        err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
+        return true;
+    }
+    let mut suggest_restrict = |span| {
+        err.span_suggestion_verbose(
+            span,
+            MSG_RESTRICT_BOUND_FURTHER,
+            format!(" + {}", constraint),
+            Applicability::MachineApplicable,
+        );
+    };
+
+    if param_name.starts_with("impl ") {
+        // If there's an `impl Trait` used in argument position, suggest
+        // restricting it:
+        //
+        //   fn foo(t: impl Foo) { ... }
+        //             --------
+        //             |
+        //             help: consider further restricting this bound with `+ Bar`
+        //
+        // Suggestion for tools in this case is:
+        //
+        //   fn foo(t: impl Foo) { ... }
+        //             --------
+        //             |
+        //             replace with: `impl Foo + Bar`
+
+        suggest_restrict(param.span.shrink_to_hi());
+        return true;
+    }
+
+    if generics.where_clause.predicates.is_empty()
+        // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
+        // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
+        && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
+    {
+        if let Some(bounds_span) = param.bounds_span() {
+            // If user has provided some bounds, suggest restricting them:
+            //
+            //   fn foo<T: Foo>(t: T) { ... }
+            //             ---
+            //             |
+            //             help: consider further restricting this bound with `+ Bar`
+            //
+            // Suggestion for tools in this case is:
+            //
+            //   fn foo<T: Foo>(t: T) { ... }
+            //          --
+            //          |
+            //          replace with: `T: Bar +`
+            suggest_restrict(bounds_span.shrink_to_hi());
+        } else {
+            // If user hasn't provided any bounds, suggest adding a new one:
+            //
+            //   fn foo<T>(t: T) { ... }
+            //          - help: consider restricting this type parameter with `T: Foo`
+            err.span_suggestion_verbose(
+                param.span.shrink_to_hi(),
+                &msg_restrict_type,
+                format!(": {}", constraint),
+                Applicability::MachineApplicable,
+            );
+        }
+
+        true
+    } else {
+        // This part is a bit tricky, because using the `where` clause user can
+        // provide zero, one or many bounds for the same type parameter, so we
+        // have following cases to consider:
+        //
+        // 1) When the type parameter has been provided zero bounds
+        //
+        //    Message:
+        //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
+        //             - help: consider restricting this type parameter with `where X: Bar`
+        //
+        //    Suggestion:
+        //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
+        //                                           - insert: `, X: Bar`
+        //
+        //
+        // 2) When the type parameter has been provided one bound
+        //
+        //    Message:
+        //      fn foo<T>(t: T) where T: Foo { ... }
+        //                            ^^^^^^
+        //                            |
+        //                            help: consider further restricting this bound with `+ Bar`
+        //
+        //    Suggestion:
+        //      fn foo<T>(t: T) where T: Foo { ... }
+        //                            ^^
+        //                            |
+        //                            replace with: `T: Bar +`
+        //
+        //
+        // 3) When the type parameter has been provided many bounds
+        //
+        //    Message:
+        //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
+        //             - help: consider further restricting this type parameter with `where T: Zar`
+        //
+        //    Suggestion:
+        //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
+        //                                          - insert: `, T: Zar`
+
+        let mut param_spans = Vec::new();
+
+        for predicate in generics.where_clause.predicates {
+            if let WherePredicate::BoundPredicate(WhereBoundPredicate {
+                span, bounded_ty, ..
+            }) = predicate
+            {
+                if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
+                    if let Some(segment) = path.segments.first() {
+                        if segment.ident.to_string() == param_name {
+                            param_spans.push(span);
+                        }
+                    }
+                }
+            }
+        }
+
+        let where_clause_span = generics.where_clause.span_for_predicates_or_empty_place();
+        // Account for `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
+        let mut trailing_comma = false;
+        if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(where_clause_span) {
+            trailing_comma = snippet.ends_with(',');
+        }
+        let where_clause_span = if trailing_comma {
+            let hi = where_clause_span.hi();
+            Span::new(hi - BytePos(1), hi, where_clause_span.ctxt())
+        } else {
+            where_clause_span.shrink_to_hi()
+        };
+
+        match &param_spans[..] {
+            &[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
+            _ => {
+                err.span_suggestion_verbose(
+                    where_clause_span,
+                    &msg_restrict_type_further,
+                    format!(", {}: {}", param_name, constraint),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+
+        true
+    }
+}
diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs
index 4e1a8b0e92f..f3b6a53dfeb 100644
--- a/src/librustc_middle/ty/error.rs
+++ b/src/librustc_middle/ty/error.rs
@@ -1,4 +1,5 @@
 use crate::traits::{ObligationCause, ObligationCauseCode};
+use crate::ty::diagnostics::suggest_constraining_type_param;
 use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt};
 use rustc_ast::ast;
 use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
@@ -401,8 +402,46 @@ impl<'tcx> TyCtxt<'tcx> {
                     (ty::Projection(_), ty::Projection(_)) => {
                         db.note("an associated type was expected, but a different one was found");
                     }
-                    (ty::Param(_), ty::Projection(_)) | (ty::Projection(_), ty::Param(_)) => {
-                        db.note("you might be missing a type parameter or trait bound");
+                    (ty::Param(p), ty::Projection(proj)) | (ty::Projection(proj), ty::Param(p)) => {
+                        let generics = self.generics_of(body_owner_def_id);
+                        let p_span = self.def_span(generics.type_param(p, self).def_id);
+                        if !sp.contains(p_span) {
+                            db.span_label(p_span, "this type parameter");
+                        }
+                        let hir = self.hir();
+                        let mut note = true;
+                        if let Some(generics) = generics
+                            .type_param(p, self)
+                            .def_id
+                            .as_local()
+                            .map(|id| hir.as_local_hir_id(id))
+                            .and_then(|id| self.hir().find(self.hir().get_parent_node(id)))
+                            .as_ref()
+                            .and_then(|node| node.generics())
+                        {
+                            // Synthesize the associated type restriction `Add<Output = Expected>`.
+                            // FIXME: extract this logic for use in other diagnostics.
+                            let trait_ref = proj.trait_ref(self);
+                            let path =
+                                self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
+                            let item_name = self.item_name(proj.item_def_id);
+                            let path = if path.ends_with('>') {
+                                format!("{}, {} = {}>", &path[..path.len() - 1], item_name, p)
+                            } else {
+                                format!("{}<{} = {}>", path, item_name, p)
+                            };
+                            note = !suggest_constraining_type_param(
+                                self,
+                                generics,
+                                db,
+                                &format!("{}", proj.self_ty()),
+                                &path,
+                                None,
+                            );
+                        }
+                        if note {
+                            db.note("you might be missing a type parameter or trait bound");
+                        }
                     }
                     (ty::Param(p), ty::Dynamic(..) | ty::Opaque(..))
                     | (ty::Dynamic(..) | ty::Opaque(..), ty::Param(p)) => {
diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs
index 5bc9f6df889..14a094b9d52 100644
--- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs
+++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs
@@ -10,10 +10,9 @@ use rustc_middle::mir::{
     FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
     ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
 };
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
 use rustc_span::source_map::DesugaringKind;
 use rustc_span::Span;
-use rustc_trait_selection::traits::error_reporting::suggest_constraining_type_param;
 
 use crate::dataflow::drop_flag_effects;
 use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex};
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index cc9fc170f6e..db836d88dd0 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -327,8 +327,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn read_str(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> {
         let len = mplace.len(self)?;
         let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len))?;
-        let str = ::std::str::from_utf8(bytes)
-            .map_err(|err| err_ub_format!("this string is not valid UTF-8: {}", err))?;
+        let str = ::std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?;
         Ok(str)
     }
 
diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs
index 7edd787c986..b9f9d37df76 100644
--- a/src/librustc_mir/interpret/traits.rs
+++ b/src/librustc_mir/interpret/traits.rs
@@ -147,14 +147,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // The drop function takes `*mut T` where `T` is the type being dropped, so get that.
         let args = fn_sig.inputs();
         if args.len() != 1 {
-            throw_ub_format!("drop fn should have 1 argument, but signature is {:?}", fn_sig);
+            throw_ub!(InvalidDropFn(fn_sig));
         }
-        let ty = args[0]
-            .builtin_deref(true)
-            .ok_or_else(|| {
-                err_ub_format!("drop fn argument type {} is not a pointer type", args[0])
-            })?
-            .ty;
+        let ty = args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidDropFn(fn_sig)))?.ty;
         Ok((drop_instance, ty))
     }
 
diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs
index b6991349ff4..9f2e79bbee3 100644
--- a/src/librustc_mir/interpret/validity.rs
+++ b/src/librustc_mir/interpret/validity.rs
@@ -25,43 +25,39 @@ use super::{
 };
 
 macro_rules! throw_validation_failure {
-    ($what:expr, $where:expr $(, $expected:expr )?) => {{
-        let mut msg = format!("encountered {}", $what);
+    ($where:expr, { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )?) => {{
+        let mut msg = String::new();
+        msg.push_str("encountered ");
+        write!(&mut msg, $($what_fmt),+).unwrap();
         let where_ = &$where;
         if !where_.is_empty() {
             msg.push_str(" at ");
             write_path(&mut msg, where_);
         }
-        $( write!(&mut msg, ", but expected {}", $expected).unwrap(); )?
+        $(
+            msg.push_str(", but expected ");
+            write!(&mut msg, $($expected_fmt),+).unwrap();
+        )?
         throw_ub!(ValidationFailure(msg))
     }};
 }
 
-/// Returns a validation failure for any Err value of $e.
-// FIXME: Replace all usages of try_validation! with try_validation_pat!.
-macro_rules! try_validation {
-    ($e:expr, $what:expr, $where:expr $(, $expected:expr )?) => {{
-        try_validation_pat!($e, $where, {
-            _ => { "{}", $what } $( expected { "{}", $expected } )?,
-        })
-    }};
-}
-/// Like try_validation, but will throw a validation error if any of the patterns in $p are
-/// matched. Other errors are passed back to the caller, unchanged. This lets you use the patterns
-/// as a kind of validation blacklist:
+/// If $e throws an error matching the pattern, throw a validation failure.
+/// Other errors are passed back to the caller, unchanged -- and if they reach the root of
+/// the visitor, we make sure only validation errors and `InvalidProgram` errors are left.
+/// This lets you use the patterns as a kind of validation whitelist, asserting which errors
+/// can possibly happen:
 ///
 /// ```
-/// let v = try_validation_pat!(some_fn(), some_path, {
+/// let v = try_validation!(some_fn(), some_path, {
 ///     Foo | Bar | Baz => { "some failure" },
 /// });
-/// // Failures that match $p are thrown up as validation errors, but other errors are passed back
-/// // unchanged.
 /// ```
 ///
 /// An additional expected parameter can also be added to the failure message:
 ///
 /// ```
-/// let v = try_validation_pat!(some_fn(), some_path, {
+/// let v = try_validation!(some_fn(), some_path, {
 ///     Foo | Bar | Baz => { "some failure" } expected { "something that wasn't a failure" },
 /// });
 /// ```
@@ -70,24 +66,25 @@ macro_rules! try_validation {
 /// the format string in directly:
 ///
 /// ```
-/// let v = try_validation_pat!(some_fn(), some_path, {
+/// let v = try_validation!(some_fn(), some_path, {
 ///     Foo | Bar | Baz => { "{:?}", some_failure } expected { "{}", expected_value },
 /// });
 /// ```
 ///
-macro_rules! try_validation_pat {
-    ($e:expr, $where:expr, { $( $p:pat )|+ =>
-        { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? $( , )?}) => {{
+macro_rules! try_validation {
+    ($e:expr, $where:expr,
+     $( $( $p:pat )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? ),+ $(,)?
+    ) => {{
         match $e {
             Ok(x) => x,
             // We catch the error and turn it into a validation failure. We are okay with
             // allocation here as this can only slow down builds that fail anyway.
-            $( Err(InterpErrorInfo { kind: $p, .. }) )|+ =>
+            $( $( Err(InterpErrorInfo { kind: $p, .. }) )|+ =>
                 throw_validation_failure!(
-                    format_args!($( $what_fmt ),+),
-                    $where
-                    $(, format_args!($( $expected_fmt ),+))?
+                    $where,
+                    { $( $what_fmt ),+ } $( expected { $( $expected_fmt ),+ } )?
                 ),
+            )+
             #[allow(unreachable_patterns)]
             Err(e) => Err::<!, _>(e)?,
         }
@@ -303,32 +300,45 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         match tail.kind {
             ty::Dynamic(..) => {
                 let vtable = meta.unwrap_meta();
+                // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
                 try_validation!(
-                    self.ecx.memory.check_ptr_access(
+                    self.ecx.memory.check_ptr_access_align(
                         vtable,
                         3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align
-                        self.ecx.tcx.data_layout.pointer_align.abi,
+                        Some(self.ecx.tcx.data_layout.pointer_align.abi),
+                        CheckInAllocMsg::InboundsTest,
                     ),
-                    "dangling or unaligned vtable pointer in wide pointer or too small vtable",
-                    self.path
+                    self.path,
+                    err_ub!(DanglingIntPointer(..)) |
+                    err_ub!(PointerUseAfterFree(..)) |
+                    err_unsup!(ReadBytesAsPointer) =>
+                        { "dangling vtable pointer in wide pointer" },
+                    err_ub!(AlignmentCheckFailed { .. }) =>
+                        { "unaligned vtable pointer in wide pointer" },
+                    err_ub!(PointerOutOfBounds { .. }) =>
+                        { "too small vtable" },
                 );
                 try_validation!(
                     self.ecx.read_drop_type_from_vtable(vtable),
-                    "invalid drop fn in vtable",
-                    self.path
+                    self.path,
+                    err_ub!(InvalidDropFn(..)) |
+                    err_ub!(DanglingIntPointer(..)) |
+                    err_ub!(InvalidFunctionPointer(..)) |
+                    err_unsup!(ReadBytesAsPointer) =>
+                        { "invalid drop function pointer in vtable" },
                 );
                 try_validation!(
                     self.ecx.read_size_and_align_from_vtable(vtable),
-                    "invalid size or align in vtable",
-                    self.path
+                    self.path,
+                    err_unsup!(ReadPointerAsBytes) => { "invalid size or align in vtable" },
                 );
                 // FIXME: More checks for the vtable.
             }
             ty::Slice(..) | ty::Str => {
                 let _len = try_validation!(
                     meta.unwrap_meta().to_machine_usize(self.ecx),
-                    "non-integer slice length in wide pointer",
-                    self.path
+                    self.path,
+                    err_unsup!(ReadPointerAsBytes) => { "non-integer slice length in wide pointer" },
                 );
                 // We do not check that `len * elem_size <= isize::MAX`:
                 // that is only required for references, and there it falls out of the
@@ -354,78 +364,52 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         // Check metadata early, for better diagnostics
         let place = try_validation!(
             self.ecx.ref_to_mplace(value),
-            format_args!("uninitialized {}", kind),
-            self.path
+            self.path,
+            err_ub!(InvalidUndefBytes(..)) => { "uninitialized {}", kind },
         );
         if place.layout.is_unsized() {
             self.check_wide_ptr_meta(place.meta, place.layout)?;
         }
         // Make sure this is dereferenceable and all.
-        let size_and_align = match self.ecx.size_and_align_of(place.meta, place.layout) {
-            Ok(res) => res,
-            Err(err) => match err.kind {
-                err_ub!(InvalidMeta(msg)) => throw_validation_failure!(
-                    format_args!("invalid {} metadata: {}", kind, msg),
-                    self.path
-                ),
-                _ => bug!("unexpected error during ptr size_and_align_of: {}", err),
-            },
-        };
+        let size_and_align = try_validation!(
+            self.ecx.size_and_align_of(place.meta, place.layout),
+            self.path,
+            err_ub!(InvalidMeta(msg)) => { "invalid {} metadata: {}", kind, msg },
+        );
         let (size, align) = size_and_align
             // for the purpose of validity, consider foreign types to have
             // alignment and size determined by the layout (size will be 0,
             // alignment should take attributes into account).
             .unwrap_or_else(|| (place.layout.size, place.layout.align.abi));
-        let ptr: Option<_> = match self.ecx.memory.check_ptr_access_align(
-            place.ptr,
-            size,
-            Some(align),
-            CheckInAllocMsg::InboundsTest,
-        ) {
-            Ok(ptr) => ptr,
-            Err(err) => {
-                info!(
-                    "{:?} did not pass access check for size {:?}, align {:?}",
-                    place.ptr, size, align
-                );
-                match err.kind {
-                    err_ub!(DanglingIntPointer(0, _)) => {
-                        throw_validation_failure!(format_args!("a NULL {}", kind), self.path)
-                    }
-                    err_ub!(DanglingIntPointer(i, _)) => throw_validation_failure!(
-                        format_args!("a {} to unallocated address {}", kind, i),
-                        self.path
-                    ),
-                    err_ub!(AlignmentCheckFailed { required, has }) => throw_validation_failure!(
-                        format_args!(
-                            "an unaligned {} (required {} byte alignment but found {})",
-                            kind,
-                            required.bytes(),
-                            has.bytes()
-                        ),
-                        self.path
-                    ),
-                    err_unsup!(ReadBytesAsPointer) => throw_validation_failure!(
-                        format_args!("a dangling {} (created from integer)", kind),
-                        self.path
-                    ),
-                    err_ub!(PointerOutOfBounds { .. }) => throw_validation_failure!(
-                        format_args!(
-                            "a dangling {} (going beyond the bounds of its allocation)",
-                            kind
-                        ),
-                        self.path
-                    ),
-                    // This cannot happen during const-eval (because interning already detects
-                    // dangling pointers), but it can happen in Miri.
-                    err_ub!(PointerUseAfterFree(_)) => throw_validation_failure!(
-                        format_args!("a dangling {} (use-after-free)", kind),
-                        self.path
-                    ),
-                    _ => bug!("Unexpected error during ptr inbounds test: {}", err),
-                }
-            }
-        };
+        // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
+        let ptr: Option<_> = try_validation!(
+            self.ecx.memory.check_ptr_access_align(
+                place.ptr,
+                size,
+                Some(align),
+                CheckInAllocMsg::InboundsTest,
+            ),
+            self.path,
+            err_ub!(AlignmentCheckFailed { required, has }) =>
+                {
+                    "an unaligned {} (required {} byte alignment but found {})",
+                    kind,
+                    required.bytes(),
+                    has.bytes()
+                },
+            err_ub!(DanglingIntPointer(0, _)) =>
+                { "a NULL {}", kind },
+            err_ub!(DanglingIntPointer(i, _)) =>
+                { "a dangling {} (address {} is unallocated)", kind, i },
+            err_ub!(PointerOutOfBounds { .. }) =>
+                { "a dangling {} (going beyond the bounds of its allocation)", kind },
+            err_unsup!(ReadBytesAsPointer) =>
+                { "a dangling {} (created from integer)", kind },
+            // This cannot happen during const-eval (because interning already detects
+            // dangling pointers), but it can happen in Miri.
+            err_ub!(PointerUseAfterFree(..)) =>
+                { "a dangling {} (use-after-free)", kind },
+        );
         // Recursive checking
         if let Some(ref mut ref_tracking) = self.ref_tracking_for_consts {
             if let Some(ptr) = ptr {
@@ -440,9 +424,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                     // We also need to do it here instead of going on to avoid running
                     // into the `before_access_global` check during validation.
                     if !self.may_ref_to_static && self.ecx.tcx.is_static(did) {
-                        throw_validation_failure!(
-                            format_args!("a {} pointing to a static variable", kind),
-                            self.path
+                        throw_validation_failure!(self.path,
+                            { "a {} pointing to a static variable", kind }
                         );
                     }
                     // `extern static` cannot be validated as they have no body.
@@ -489,12 +472,20 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         match ty.kind {
             ty::Bool => {
                 let value = self.ecx.read_scalar(value)?;
-                try_validation!(value.to_bool(), value, self.path, "a boolean");
+                try_validation!(
+                    value.to_bool(),
+                    self.path,
+                    err_ub!(InvalidBool(..)) => { "{}", value } expected { "a boolean" },
+                );
                 Ok(true)
             }
             ty::Char => {
                 let value = self.ecx.read_scalar(value)?;
-                try_validation!(value.to_char(), value, self.path, "a valid unicode codepoint");
+                try_validation!(
+                    value.to_char(),
+                    self.path,
+                    err_ub!(InvalidChar(..)) => { "{}", value } expected { "a valid unicode codepoint" },
+                );
                 Ok(true)
             }
             ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
@@ -505,10 +496,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                     // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous
                     let is_bits = value.not_undef().map_or(false, |v| v.is_bits());
                     if !is_bits {
-                        throw_validation_failure!(
-                            value,
-                            self.path,
-                            "initialized plain (non-pointer) bytes"
+                        throw_validation_failure!(self.path,
+                            { "{}", value } expected { "initialized plain (non-pointer) bytes" }
                         )
                     }
                 } else {
@@ -521,9 +510,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                 // We are conservative with undef for integers, but try to
                 // actually enforce the strict rules for raw pointers (mostly because
                 // that lets us re-use `ref_to_mplace`).
-                let place = try_validation_pat!(self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?), self.path, {
+                let place = try_validation!(
+                    self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?),
+                    self.path,
                     err_ub!(InvalidUndefBytes(..)) => { "uninitialized raw pointer" },
-                });
+                );
                 if place.layout.is_unsized() {
                     self.check_wide_ptr_meta(place.meta, place.layout)?;
                 }
@@ -541,14 +532,16 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                 let value = self.ecx.read_scalar(value)?;
                 let _fn = try_validation!(
                     value.not_undef().and_then(|ptr| self.ecx.memory.get_fn(ptr)),
-                    value,
                     self.path,
-                    "a function pointer"
+                    err_ub!(DanglingIntPointer(..)) |
+                    err_ub!(InvalidFunctionPointer(..)) |
+                    err_unsup!(ReadBytesAsPointer) =>
+                        { "{}", value } expected { "a function pointer" },
                 );
                 // FIXME: Check if the signature matches
                 Ok(true)
             }
-            ty::Never => throw_validation_failure!("a value of the never type `!`", self.path),
+            ty::Never => throw_validation_failure!(self.path, { "a value of the never type `!`" }),
             ty::Foreign(..) | ty::FnDef(..) => {
                 // Nothing to check.
                 Ok(true)
@@ -598,35 +591,33 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         // At least one value is excluded. Get the bits.
         let value = try_validation!(
             value.not_undef(),
-            value,
             self.path,
-            format_args!("something {}", wrapping_range_format(valid_range, max_hi),)
+            err_ub!(InvalidUndefBytes(..)) => { "{}", value }
+                expected { "something {}", wrapping_range_format(valid_range, max_hi) },
         );
         let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) {
             Err(ptr) => {
                 if lo == 1 && hi == max_hi {
                     // Only NULL is the niche.  So make sure the ptr is NOT NULL.
                     if self.ecx.memory.ptr_may_be_null(ptr) {
-                        throw_validation_failure!(
-                            "a potentially NULL pointer",
-                            self.path,
-                            format_args!(
+                        throw_validation_failure!(self.path,
+                            { "a potentially NULL pointer" }
+                            expected {
                                 "something that cannot possibly fail to be {}",
                                 wrapping_range_format(valid_range, max_hi)
-                            )
+                            }
                         )
                     }
                     return Ok(());
                 } else {
                     // Conservatively, we reject, because the pointer *could* have a bad
                     // value.
-                    throw_validation_failure!(
-                        "a pointer",
-                        self.path,
-                        format_args!(
+                    throw_validation_failure!(self.path,
+                        { "a pointer" }
+                        expected {
                             "something that cannot possibly fail to be {}",
                             wrapping_range_format(valid_range, max_hi)
-                        )
+                        }
                     )
                 }
             }
@@ -636,10 +627,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         if wrapping_range_contains(&valid_range, bits) {
             Ok(())
         } else {
-            throw_validation_failure!(
-                bits,
-                self.path,
-                format_args!("something {}", wrapping_range_format(valid_range, max_hi))
+            throw_validation_failure!(self.path,
+                { "{}", bits }
+                expected { "something {}", wrapping_range_format(valid_range, max_hi) }
             )
         }
     }
@@ -703,19 +693,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
         assert!(op.layout.ty.builtin_deref(true).is_none());
 
         // Recursively walk the type. Translate some possible errors to something nicer.
-        match self.walk_value(op) {
-            Ok(()) => {}
-            Err(err) => match err.kind {
-                err_ub!(InvalidDiscriminant(val)) => {
-                    throw_validation_failure!(val, self.path, "a valid enum discriminant")
-                }
-                err_unsup!(ReadPointerAsBytes) => {
-                    throw_validation_failure!("a pointer", self.path, "plain (non-pointer) bytes")
-                }
-                // Propagate upwards (that will also check for unexpected errors).
-                _ => return Err(err),
-            },
-        }
+        try_validation!(
+            self.walk_value(op),
+            self.path,
+            err_ub!(InvalidDiscriminant(val)) =>
+                { "{}", val } expected { "a valid enum discriminant" },
+            err_unsup!(ReadPointerAsBytes) =>
+                { "a pointer" } expected { "plain (non-pointer) bytes" },
+        );
 
         // *After* all of this, check the ABI.  We need to check the ABI to handle
         // types like `NonNull` where the `Scalar` info is more restrictive than what
@@ -729,9 +714,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
         // MyNewtype and then the scalar in there).
         match op.layout.abi {
             Abi::Uninhabited => {
-                throw_validation_failure!(
-                    format_args!("a value of uninhabited type {:?}", op.layout.ty),
-                    self.path
+                throw_validation_failure!(self.path,
+                    { "a value of uninhabited type {:?}", op.layout.ty }
                 );
             }
             Abi::Scalar(ref scalar_layout) => {
@@ -761,8 +745,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 let mplace = op.assert_mem_place(self.ecx); // strings are never immediate
                 try_validation!(
                     self.ecx.read_str(mplace),
-                    "uninitialized or non-UTF-8 data in str",
-                    self.path
+                    self.path,
+                    err_ub!(InvalidStr(..)) => { "uninitialized or non-UTF-8 data in str" },
                 );
             }
             ty::Array(tys, ..) | ty::Slice(tys)
@@ -815,7 +799,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                     Ok(()) => {}
                     // Some error happened, try to provide a more detailed description.
                     Err(err) => {
-                        // For some errors we might be able to provide extra information
+                        // For some errors we might be able to provide extra information.
+                        // (This custom logic does not fit the `try_validation!` macro.)
                         match err.kind {
                             err_ub!(InvalidUndefBytes(Some(ptr))) => {
                                 // Some byte was uninitialized, determine which
@@ -825,7 +810,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                                     .unwrap();
                                 self.path.push(PathElem::ArrayElem(i));
 
-                                throw_validation_failure!("uninitialized bytes", self.path)
+                                throw_validation_failure!(self.path, { "uninitialized bytes" })
                             }
                             // Propagate upwards (that will also check for unexpected errors).
                             _ => return Err(err),
@@ -876,7 +861,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             // validate and each caller will know best what to do with them.
             Err(err) if matches!(err.kind, InterpError::InvalidProgram(_)) => Err(err),
             // Avoid other errors as those do not show *where* in the value the issue lies.
-            Err(err) => bug!("Unexpected error during validation: {}", err),
+            Err(err) => {
+                err.print_backtrace();
+                bug!("Unexpected error during validation: {}", err);
+            }
         }
     }
 
diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs
index fa2af24c945..19ed6b50f92 100644
--- a/src/librustc_trait_selection/traits/error_reporting/mod.rs
+++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs
@@ -15,17 +15,16 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_hir::{Node, QPath, TyKind, WhereBoundPredicate, WherePredicate};
+use rustc_hir::Node;
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::error::ExpectedFound;
-use rustc_middle::ty::fast_reject;
 use rustc_middle::ty::fold::TypeFolder;
-use rustc_middle::ty::SubtypePredicate;
 use rustc_middle::ty::{
-    self, AdtKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
+    self, fast_reject, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt,
+    TypeFoldable, WithConstness,
 };
 use rustc_session::DiagnosticMessageId;
-use rustc_span::{BytePos, ExpnKind, Span, DUMMY_SP};
+use rustc_span::{ExpnKind, Span, DUMMY_SP};
 use std::fmt;
 
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -1700,180 +1699,3 @@ impl ArgKind {
         }
     }
 }
-
-/// Suggest restricting a type param with a new bound.
-pub fn suggest_constraining_type_param(
-    tcx: TyCtxt<'_>,
-    generics: &hir::Generics<'_>,
-    err: &mut DiagnosticBuilder<'_>,
-    param_name: &str,
-    constraint: &str,
-    def_id: Option<DefId>,
-) -> bool {
-    let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
-
-    let param = if let Some(param) = param {
-        param
-    } else {
-        return false;
-    };
-
-    const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
-    let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
-    let msg_restrict_type_further =
-        format!("consider further restricting type parameter `{}`", param_name);
-
-    if def_id == tcx.lang_items().sized_trait() {
-        // Type parameters are already `Sized` by default.
-        err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
-        return true;
-    }
-    let mut suggest_restrict = |span| {
-        err.span_suggestion_verbose(
-            span,
-            MSG_RESTRICT_BOUND_FURTHER,
-            format!(" + {}", constraint),
-            Applicability::MachineApplicable,
-        );
-    };
-
-    if param_name.starts_with("impl ") {
-        // If there's an `impl Trait` used in argument position, suggest
-        // restricting it:
-        //
-        //   fn foo(t: impl Foo) { ... }
-        //             --------
-        //             |
-        //             help: consider further restricting this bound with `+ Bar`
-        //
-        // Suggestion for tools in this case is:
-        //
-        //   fn foo(t: impl Foo) { ... }
-        //             --------
-        //             |
-        //             replace with: `impl Foo + Bar`
-
-        suggest_restrict(param.span.shrink_to_hi());
-        return true;
-    }
-
-    if generics.where_clause.predicates.is_empty()
-        // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
-        // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
-        && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
-    {
-        if let Some(bounds_span) = param.bounds_span() {
-            // If user has provided some bounds, suggest restricting them:
-            //
-            //   fn foo<T: Foo>(t: T) { ... }
-            //             ---
-            //             |
-            //             help: consider further restricting this bound with `+ Bar`
-            //
-            // Suggestion for tools in this case is:
-            //
-            //   fn foo<T: Foo>(t: T) { ... }
-            //          --
-            //          |
-            //          replace with: `T: Bar +`
-            suggest_restrict(bounds_span.shrink_to_hi());
-        } else {
-            // If user hasn't provided any bounds, suggest adding a new one:
-            //
-            //   fn foo<T>(t: T) { ... }
-            //          - help: consider restricting this type parameter with `T: Foo`
-            err.span_suggestion_verbose(
-                param.span.shrink_to_hi(),
-                &msg_restrict_type,
-                format!(": {}", constraint),
-                Applicability::MachineApplicable,
-            );
-        }
-
-        true
-    } else {
-        // This part is a bit tricky, because using the `where` clause user can
-        // provide zero, one or many bounds for the same type parameter, so we
-        // have following cases to consider:
-        //
-        // 1) When the type parameter has been provided zero bounds
-        //
-        //    Message:
-        //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
-        //             - help: consider restricting this type parameter with `where X: Bar`
-        //
-        //    Suggestion:
-        //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
-        //                                           - insert: `, X: Bar`
-        //
-        //
-        // 2) When the type parameter has been provided one bound
-        //
-        //    Message:
-        //      fn foo<T>(t: T) where T: Foo { ... }
-        //                            ^^^^^^
-        //                            |
-        //                            help: consider further restricting this bound with `+ Bar`
-        //
-        //    Suggestion:
-        //      fn foo<T>(t: T) where T: Foo { ... }
-        //                            ^^
-        //                            |
-        //                            replace with: `T: Bar +`
-        //
-        //
-        // 3) When the type parameter has been provided many bounds
-        //
-        //    Message:
-        //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
-        //             - help: consider further restricting this type parameter with `where T: Zar`
-        //
-        //    Suggestion:
-        //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
-        //                                          - insert: `, T: Zar`
-
-        let mut param_spans = Vec::new();
-
-        for predicate in generics.where_clause.predicates {
-            if let WherePredicate::BoundPredicate(WhereBoundPredicate {
-                span, bounded_ty, ..
-            }) = predicate
-            {
-                if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
-                    if let Some(segment) = path.segments.first() {
-                        if segment.ident.to_string() == param_name {
-                            param_spans.push(span);
-                        }
-                    }
-                }
-            }
-        }
-
-        let where_clause_span = generics.where_clause.span_for_predicates_or_empty_place();
-        // Account for `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
-        let mut trailing_comma = false;
-        if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(where_clause_span) {
-            trailing_comma = snippet.ends_with(',');
-        }
-        let where_clause_span = if trailing_comma {
-            let hi = where_clause_span.hi();
-            Span::new(hi - BytePos(1), hi, where_clause_span.ctxt())
-        } else {
-            where_clause_span.shrink_to_hi()
-        };
-
-        match &param_spans[..] {
-            &[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
-            _ => {
-                err.span_suggestion_verbose(
-                    where_clause_span,
-                    &msg_restrict_type_further,
-                    format!(", {}: {}", param_name, constraint),
-                    Applicability::MachineApplicable,
-                );
-            }
-        }
-
-        true
-    }
-}
diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
index ce7b1390d46..74dd47a91c2 100644
--- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
+++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
@@ -3,7 +3,6 @@ use super::{
 };
 
 use crate::infer::InferCtxt;
-use crate::traits::error_reporting::suggest_constraining_type_param;
 
 use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style};
 use rustc_hir as hir;
@@ -13,7 +12,8 @@ use rustc_hir::intravisit::Visitor;
 use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
 use rustc_middle::ty::TypeckTables;
 use rustc_middle::ty::{
-    self, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
+    self, suggest_constraining_type_param, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty,
+    TyCtxt, TypeFoldable, WithConstness,
 };
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::{MultiSpan, Span, DUMMY_SP};
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 228c40ac853..857cc972559 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -947,65 +947,59 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // this isn't perfect (that is, there are cases when
                 // implementing a trait would be legal but is rejected
                 // here).
-                !unsatisfied_predicates.iter().any(|(p, _)| match p {
-                        // Hide traits if they are present in predicates as they can be fixed without
-                        // having to implement them.
-                        ty::Predicate::Trait(t, _) => t.def_id() != info.def_id,
-                        ty::Predicate::Projection(p) => p.item_def_id() != info.def_id,
-                        _ => true,
-                    }) && (type_is_local || info.def_id.is_local())
-                        && self
-                            .associated_item(info.def_id, item_name, Namespace::ValueNS)
-                            .filter(|item| {
-                                if let ty::AssocKind::Fn = item.kind {
-                                    let id = item.def_id.as_local().map(|def_id| {
-                                        self.tcx.hir().as_local_hir_id(def_id)
-                                    });
-                                    if let Some(hir::Node::TraitItem(hir::TraitItem {
-                                        kind: hir::TraitItemKind::Fn(fn_sig, method),
-                                        ..
-                                    })) = id.map(|id| self.tcx.hir().get(id))
+                unsatisfied_predicates.iter().all(|(p, _)| match p {
+                    // Hide traits if they are present in predicates as they can be fixed without
+                    // having to implement them.
+                    ty::Predicate::Trait(t, _) => t.def_id() == info.def_id,
+                    ty::Predicate::Projection(p) => p.item_def_id() == info.def_id,
+                    _ => false,
+                }) && (type_is_local || info.def_id.is_local())
+                    && self
+                        .associated_item(info.def_id, item_name, Namespace::ValueNS)
+                        .filter(|item| {
+                            if let ty::AssocKind::Fn = item.kind {
+                                let id = item
+                                    .def_id
+                                    .as_local()
+                                    .map(|def_id| self.tcx.hir().as_local_hir_id(def_id));
+                                if let Some(hir::Node::TraitItem(hir::TraitItem {
+                                    kind: hir::TraitItemKind::Fn(fn_sig, method),
+                                    ..
+                                })) = id.map(|id| self.tcx.hir().get(id))
+                                {
+                                    let self_first_arg = match method {
+                                        hir::TraitFn::Required([ident, ..]) => {
+                                            ident.name == kw::SelfLower
+                                        }
+                                        hir::TraitFn::Provided(body_id) => {
+                                            self.tcx.hir().body(*body_id).params.first().map_or(
+                                                false,
+                                                |param| {
+                                                    matches!(
+                                                        param.pat.kind,
+                                                        hir::PatKind::Binding(_, _, ident, _)
+                                                            if ident.name == kw::SelfLower
+                                                    )
+                                                },
+                                            )
+                                        }
+                                        _ => false,
+                                    };
+
+                                    if !fn_sig.decl.implicit_self.has_implicit_self()
+                                        && self_first_arg
                                     {
-                                        let self_first_arg = match method {
-                                            hir::TraitFn::Required([ident, ..]) => {
-                                                ident.name == kw::SelfLower
-                                            }
-                                            hir::TraitFn::Provided(body_id) => {
-                                                match &self.tcx.hir().body(*body_id).params[..] {
-                                                    [hir::Param {
-                                                        pat:
-                                                            hir::Pat {
-                                                                kind:
-                                                                    hir::PatKind::Binding(
-                                                                        _,
-                                                                        _,
-                                                                        ident,
-                                                                        ..,
-                                                                    ),
-                                                                ..
-                                                            },
-                                                        ..
-                                                    }, ..] => ident.name == kw::SelfLower,
-                                                    _ => false,
-                                                }
-                                            }
-                                            _ => false,
-                                        };
-
-                                        if !fn_sig.decl.implicit_self.has_implicit_self()
-                                            && self_first_arg
-                                        {
-                                            if let Some(ty) = fn_sig.decl.inputs.get(0) {
-                                                arbitrary_rcvr.push(ty.span);
-                                            }
-                                            return false;
+                                        if let Some(ty) = fn_sig.decl.inputs.get(0) {
+                                            arbitrary_rcvr.push(ty.span);
                                         }
+                                        return false;
                                     }
                                 }
-                                // We only want to suggest public or local traits (#45781).
-                                item.vis == ty::Visibility::Public || info.def_id.is_local()
-                            })
-                            .is_some()
+                            }
+                            // We only want to suggest public or local traits (#45781).
+                            item.vis == ty::Visibility::Public || info.def_id.is_local()
+                        })
+                        .is_some()
             })
             .collect::<Vec<_>>();
         for span in &arbitrary_rcvr {
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 23004cf3647..e6cbc8ab723 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -10,7 +10,7 @@ use rustc_middle::ty::adjustment::{
     Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
 };
 use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
-use rustc_middle::ty::{self, Ty, TypeFoldable};
+use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable};
 use rustc_span::Span;
 use rustc_trait_selection::infer::InferCtxtExt;
 
@@ -253,6 +253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // error types are considered "builtin"
                 if !lhs_ty.references_error() && !rhs_ty.references_error() {
                     let source_map = self.tcx.sess.source_map();
+
                     match is_assign {
                         IsAssign::Yes => {
                             let mut err = struct_span_err!(
@@ -317,12 +318,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     // This has nothing here because it means we did string
                                     // concatenation (e.g., "Hello " += "World!"). This means
                                     // we don't want the note in the else clause to be emitted
-                                } else if let ty::Param(_) = lhs_ty.kind {
-                                    // FIXME: point to span of param
-                                    err.note(&format!(
-                                        "`{}` might need a bound for `{}`",
-                                        lhs_ty, missing_trait
-                                    ));
+                                } else if let ty::Param(p) = lhs_ty.kind {
+                                    suggest_constraining_param(
+                                        self.tcx,
+                                        self.body_id,
+                                        &mut err,
+                                        lhs_ty,
+                                        rhs_ty,
+                                        missing_trait,
+                                        p,
+                                        false,
+                                    );
                                 } else if !suggested_deref {
                                     suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
                                 }
@@ -330,46 +336,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             err.emit();
                         }
                         IsAssign::No => {
-                            let (message, missing_trait) = match op.node {
+                            let (message, missing_trait, use_output) = match op.node {
                                 hir::BinOpKind::Add => (
                                     format!("cannot add `{}` to `{}`", rhs_ty, lhs_ty),
                                     Some("std::ops::Add"),
+                                    true,
                                 ),
                                 hir::BinOpKind::Sub => (
                                     format!("cannot subtract `{}` from `{}`", rhs_ty, lhs_ty),
                                     Some("std::ops::Sub"),
+                                    true,
                                 ),
                                 hir::BinOpKind::Mul => (
                                     format!("cannot multiply `{}` to `{}`", rhs_ty, lhs_ty),
                                     Some("std::ops::Mul"),
+                                    true,
                                 ),
                                 hir::BinOpKind::Div => (
                                     format!("cannot divide `{}` by `{}`", lhs_ty, rhs_ty),
                                     Some("std::ops::Div"),
+                                    true,
                                 ),
                                 hir::BinOpKind::Rem => (
                                     format!("cannot mod `{}` by `{}`", lhs_ty, rhs_ty),
                                     Some("std::ops::Rem"),
+                                    true,
                                 ),
                                 hir::BinOpKind::BitAnd => (
                                     format!("no implementation for `{} & {}`", lhs_ty, rhs_ty),
                                     Some("std::ops::BitAnd"),
+                                    true,
                                 ),
                                 hir::BinOpKind::BitXor => (
                                     format!("no implementation for `{} ^ {}`", lhs_ty, rhs_ty),
                                     Some("std::ops::BitXor"),
+                                    true,
                                 ),
                                 hir::BinOpKind::BitOr => (
                                     format!("no implementation for `{} | {}`", lhs_ty, rhs_ty),
                                     Some("std::ops::BitOr"),
+                                    true,
                                 ),
                                 hir::BinOpKind::Shl => (
                                     format!("no implementation for `{} << {}`", lhs_ty, rhs_ty),
                                     Some("std::ops::Shl"),
+                                    true,
                                 ),
                                 hir::BinOpKind::Shr => (
                                     format!("no implementation for `{} >> {}`", lhs_ty, rhs_ty),
                                     Some("std::ops::Shr"),
+                                    true,
                                 ),
                                 hir::BinOpKind::Eq | hir::BinOpKind::Ne => (
                                     format!(
@@ -378,6 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                         lhs_ty
                                     ),
                                     Some("std::cmp::PartialEq"),
+                                    false,
                                 ),
                                 hir::BinOpKind::Lt
                                 | hir::BinOpKind::Le
@@ -389,6 +406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                         lhs_ty
                                     ),
                                     Some("std::cmp::PartialOrd"),
+                                    false,
                                 ),
                                 _ => (
                                     format!(
@@ -397,6 +415,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                         lhs_ty
                                     ),
                                     None,
+                                    false,
                                 ),
                             };
                             let mut err = struct_span_err!(
@@ -459,12 +478,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     // This has nothing here because it means we did string
                                     // concatenation (e.g., "Hello " + "World!"). This means
                                     // we don't want the note in the else clause to be emitted
-                                } else if let ty::Param(_) = lhs_ty.kind {
-                                    // FIXME: point to span of param
-                                    err.note(&format!(
-                                        "`{}` might need a bound for `{}`",
-                                        lhs_ty, missing_trait
-                                    ));
+                                } else if let ty::Param(p) = lhs_ty.kind {
+                                    suggest_constraining_param(
+                                        self.tcx,
+                                        self.body_id,
+                                        &mut err,
+                                        lhs_ty,
+                                        rhs_ty,
+                                        missing_trait,
+                                        p,
+                                        use_output,
+                                    );
                                 } else if !suggested_deref && !involves_fn {
                                     suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
                                 }
@@ -911,3 +935,43 @@ fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_tra
         }
     }
 }
+
+fn suggest_constraining_param(
+    tcx: TyCtxt<'_>,
+    body_id: hir::HirId,
+    mut err: &mut DiagnosticBuilder<'_>,
+    lhs_ty: Ty<'_>,
+    rhs_ty: Ty<'_>,
+    missing_trait: &str,
+    p: ty::ParamTy,
+    set_output: bool,
+) {
+    let hir = tcx.hir();
+    let msg = &format!("`{}` might need a bound for `{}`", lhs_ty, missing_trait);
+    // Try to find the def-id and details for the parameter p. We have only the index,
+    // so we have to find the enclosing function's def-id, then look through its declared
+    // generic parameters to get the declaration.
+    let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id });
+    let generics = tcx.generics_of(def_id);
+    let param_def_id = generics.type_param(&p, tcx).def_id;
+    if let Some(generics) = param_def_id
+        .as_local()
+        .map(|id| hir.as_local_hir_id(id))
+        .and_then(|id| hir.find(hir.get_parent_item(id)))
+        .as_ref()
+        .and_then(|node| node.generics())
+    {
+        let output = if set_output { format!("<Output = {}>", rhs_ty) } else { String::new() };
+        suggest_constraining_type_param(
+            tcx,
+            generics,
+            &mut err,
+            &format!("{}", lhs_ty),
+            &format!("{}{}", missing_trait, output),
+            None,
+        );
+    } else {
+        let span = tcx.def_span(param_def_id);
+        err.span_label(span, msg);
+    }
+}
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 4bb50f75791..550f672ed4c 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -1129,9 +1129,36 @@ pub struct IdMap {
     map: FxHashMap<String, usize>,
 }
 
+fn init_id_map() -> FxHashMap<String, usize> {
+    let mut map = FxHashMap::default();
+    // This is the list of IDs used by rustdoc templates.
+    map.insert("mainThemeStyle".to_owned(), 1);
+    map.insert("themeStyle".to_owned(), 1);
+    map.insert("theme-picker".to_owned(), 1);
+    map.insert("theme-choices".to_owned(), 1);
+    map.insert("settings-menu".to_owned(), 1);
+    map.insert("main".to_owned(), 1);
+    map.insert("search".to_owned(), 1);
+    map.insert("crate-search".to_owned(), 1);
+    map.insert("render-detail".to_owned(), 1);
+    map.insert("toggle-all-docs".to_owned(), 1);
+    map.insert("all-types".to_owned(), 1);
+    // This is the list of IDs used by rustdoc sections.
+    map.insert("fields".to_owned(), 1);
+    map.insert("variants".to_owned(), 1);
+    map.insert("implementors-list".to_owned(), 1);
+    map.insert("synthetic-implementors-list".to_owned(), 1);
+    map.insert("implementations".to_owned(), 1);
+    map.insert("trait-implementations".to_owned(), 1);
+    map.insert("synthetic-implementations".to_owned(), 1);
+    map.insert("blanket-implementations".to_owned(), 1);
+    map.insert("deref-methods".to_owned(), 1);
+    map
+}
+
 impl IdMap {
     pub fn new() -> Self {
-        IdMap::default()
+        IdMap { map: init_id_map() }
     }
 
     pub fn populate<I: IntoIterator<Item = String>>(&mut self, ids: I) {
@@ -1141,7 +1168,7 @@ impl IdMap {
     }
 
     pub fn reset(&mut self) {
-        self.map = FxHashMap::default();
+        self.map = init_id_map();
     }
 
     pub fn derive(&mut self, candidate: String) -> String {
diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs
index c871587a0a9..bf0451a1d9d 100644
--- a/src/librustdoc/html/markdown/tests.rs
+++ b/src/librustdoc/html/markdown/tests.rs
@@ -29,8 +29,8 @@ fn test_unique_id() {
         "examples-2",
         "method.into_iter-1",
         "foo-1",
-        "main",
-        "search",
+        "main-1",
+        "search-1",
         "methods",
         "examples-3",
         "method.into_iter-2",
@@ -191,8 +191,8 @@ fn test_header_ids_multiple_blocks() {
     t(
         &mut map,
         "# Main",
-        "<h1 id=\"main\" class=\"section-header\">\
-        <a href=\"#main\">Main</a></h1>",
+        "<h1 id=\"main-1\" class=\"section-header\">\
+        <a href=\"#main-1\">Main</a></h1>",
     );
     t(
         &mut map,
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 387ef03f067..666e59b9a04 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -3413,8 +3413,8 @@ fn render_assoc_items(
                 write!(
                     w,
                     "\
-                    <h2 id='methods' class='small-section-header'>\
-                      Methods<a href='#methods' class='anchor'></a>\
+                    <h2 id='implementations' class='small-section-header'>\
+                      Implementations<a href='#implementations' class='anchor'></a>\
                     </h2>\
                 "
                 );
@@ -3475,10 +3475,10 @@ fn render_assoc_items(
             write!(
                 w,
                 "\
-                <h2 id='implementations' class='small-section-header'>\
-                  Trait Implementations<a href='#implementations' class='anchor'></a>\
+                <h2 id='trait-implementations' class='small-section-header'>\
+                  Trait Implementations<a href='#trait-implementations' class='anchor'></a>\
                 </h2>\
-                <div id='implementations-list'>{}</div>",
+                <div id='trait-implementations-list'>{}</div>",
                 impls
             );
         }
@@ -4097,8 +4097,8 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
             ret.sort();
             if !ret.is_empty() {
                 out.push_str(&format!(
-                    "<a class=\"sidebar-title\" href=\"#methods\">Methods\
-                                       </a><div class=\"sidebar-links\">{}</div>",
+                    "<a class=\"sidebar-title\" href=\"#implementations\">Methods</a>\
+                     <div class=\"sidebar-links\">{}</div>",
                     ret.join("")
                 ));
             }
@@ -4191,8 +4191,8 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
 
             if !concrete_format.is_empty() {
                 out.push_str(
-                    "<a class=\"sidebar-title\" href=\"#implementations\">\
-                              Trait Implementations</a>",
+                    "<a class=\"sidebar-title\" href=\"#trait-implementations\">\
+                        Trait Implementations</a>",
                 );
                 out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", concrete_format));
             }
@@ -4200,7 +4200,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
             if !synthetic_format.is_empty() {
                 out.push_str(
                     "<a class=\"sidebar-title\" href=\"#synthetic-implementations\">\
-                              Auto Trait Implementations</a>",
+                        Auto Trait Implementations</a>",
                 );
                 out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", synthetic_format));
             }
@@ -4208,7 +4208,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
             if !blanket_format.is_empty() {
                 out.push_str(
                     "<a class=\"sidebar-title\" href=\"#blanket-implementations\">\
-                              Blanket Implementations</a>",
+                        Blanket Implementations</a>",
                 );
                 out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", blanket_format));
             }
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index 3f12fb893a4..a023d5a2d95 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -2180,7 +2180,7 @@ function getSearchElement() {
         if (collapse) {
             toggleAllDocs(pageId, true);
         } else if (getCurrentValue("rustdoc-auto-hide-trait-implementations") !== "false") {
-            var impl_list = document.getElementById("implementations-list");
+            var impl_list = document.getElementById("trait-implementations-list");
 
             if (impl_list !== null) {
                 onEachLazy(impl_list.getElementsByClassName("collapse-toggle"), function(e) {
diff --git a/src/test/rustdoc/const-generics/add-impl.rs b/src/test/rustdoc/const-generics/add-impl.rs
index 54bdd768f8a..905f9588268 100644
--- a/src/test/rustdoc/const-generics/add-impl.rs
+++ b/src/test/rustdoc/const-generics/add-impl.rs
@@ -11,7 +11,7 @@ pub struct Simd<T, const WIDTH: usize> {
     inner: T,
 }
 
-// @has foo/struct.Simd.html '//div[@id="implementations-list"]/h3/code' 'impl Add<Simd<u8, 16usize>> for Simd<u8, 16>'
+// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]/h3/code' 'impl Add<Simd<u8, 16usize>> for Simd<u8, 16>'
 impl Add for Simd<u8, 16> {
     type Output = Self;
 
diff --git a/src/test/rustdoc/duplicate_impls/issue-33054.rs b/src/test/rustdoc/duplicate_impls/issue-33054.rs
index 3f7cec18563..112d632971a 100644
--- a/src/test/rustdoc/duplicate_impls/issue-33054.rs
+++ b/src/test/rustdoc/duplicate_impls/issue-33054.rs
@@ -1,7 +1,7 @@
 // @has issue_33054/impls/struct.Foo.html
 // @has - '//code' 'impl Foo'
 // @has - '//code' 'impl Bar for Foo'
-// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
+// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
 // @count - '//*[@id="main"]/*[@class="impl"]' 1
 // @has issue_33054/impls/bar/trait.Bar.html
 // @has - '//code' 'impl Bar for Foo'
diff --git a/src/test/rustdoc/issue-21474.rs b/src/test/rustdoc/issue-21474.rs
index 4c530f72b8a..896fc1a78f1 100644
--- a/src/test/rustdoc/issue-21474.rs
+++ b/src/test/rustdoc/issue-21474.rs
@@ -7,5 +7,5 @@ mod inner {
 pub trait Blah { }
 
 // @count issue_21474/struct.What.html \
-//        '//*[@id="implementations-list"]/*[@class="impl"]' 1
+//        '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
 pub struct What;
diff --git a/src/test/rustdoc/issue-45584.rs b/src/test/rustdoc/issue-45584.rs
index cd8c275d852..0225c0c5c2f 100644
--- a/src/test/rustdoc/issue-45584.rs
+++ b/src/test/rustdoc/issue-45584.rs
@@ -4,12 +4,12 @@ pub trait Bar<T, U> {}
 
 // @has 'foo/struct.Foo1.html'
 pub struct Foo1;
-// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
+// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
 // @has - '//*[@class="impl"]' "impl Bar<Foo1, &'static Foo1> for Foo1"
 impl Bar<Foo1, &'static Foo1> for Foo1 {}
 
 // @has 'foo/struct.Foo2.html'
 pub struct Foo2;
-// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
+// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
 // @has - '//*[@class="impl"]' "impl Bar<&'static Foo2, Foo2> for u8"
 impl Bar<&'static Foo2, Foo2> for u8 {}
diff --git a/src/test/rustdoc/issue-55321.rs b/src/test/rustdoc/issue-55321.rs
index 8c001db06c5..d312a511459 100644
--- a/src/test/rustdoc/issue-55321.rs
+++ b/src/test/rustdoc/issue-55321.rs
@@ -1,8 +1,8 @@
 #![feature(negative_impls)]
 
 // @has issue_55321/struct.A.html
-// @has - '//*[@id="implementations-list"]/*[@class="impl"]//code' "impl !Send for A"
-// @has - '//*[@id="implementations-list"]/*[@class="impl"]//code' "impl !Sync for A"
+// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Send for A"
+// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Sync for A"
 pub struct A();
 
 impl !Send for A {}
diff --git a/src/test/rustdoc/negative-impl-sidebar.rs b/src/test/rustdoc/negative-impl-sidebar.rs
index cb46d1778d9..3414d954077 100644
--- a/src/test/rustdoc/negative-impl-sidebar.rs
+++ b/src/test/rustdoc/negative-impl-sidebar.rs
@@ -4,6 +4,6 @@
 pub struct Foo;
 
 // @has foo/struct.Foo.html
-// @has - '//*[@class="sidebar-title"][@href="#implementations"]' 'Trait Implementations'
+// @has - '//*[@class="sidebar-title"][@href="#trait-implementations"]' 'Trait Implementations'
 // @has - '//*[@class="sidebar-links"]/a' '!Sync'
 impl !Sync for Foo {}
diff --git a/src/test/rustdoc/struct-implementations-title.rs b/src/test/rustdoc/struct-implementations-title.rs
new file mode 100644
index 00000000000..96eb11311d6
--- /dev/null
+++ b/src/test/rustdoc/struct-implementations-title.rs
@@ -0,0 +1,9 @@
+#![crate_name = "foo"]
+
+pub struct Struc;
+
+// @has foo/struct.Struc.html
+// @has - '//*[@id="main"]/h2[@id="implementations"]' "Implementations"
+impl Struc {
+    pub const S: u64 = 0;
+}
diff --git a/src/test/rustdoc/synthetic_auto/manual.rs b/src/test/rustdoc/synthetic_auto/manual.rs
index 458403462d6..d20b4744af1 100644
--- a/src/test/rustdoc/synthetic_auto/manual.rs
+++ b/src/test/rustdoc/synthetic_auto/manual.rs
@@ -2,10 +2,10 @@
 // @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' 'impl<T> Sync for \
 // Foo<T> where T: Sync'
 //
-// @has - '//*[@id="implementations-list"]/*[@class="impl"]//code' \
+// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' \
 // 'impl<T> Send for Foo<T>'
 //
-// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
+// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
 // @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 4
 pub struct Foo<T> {
     field: T,
diff --git a/src/test/rustdoc/typedef.rs b/src/test/rustdoc/typedef.rs
index 80351ff52f5..7f834d3d5a5 100644
--- a/src/test/rustdoc/typedef.rs
+++ b/src/test/rustdoc/typedef.rs
@@ -13,8 +13,8 @@ impl MyStruct {
 // @has - '//*[@class="impl"]//code' 'impl MyTrait for MyAlias'
 // @has - 'Alias docstring'
 // @has - '//*[@class="sidebar"]//p[@class="location"]' 'Type Definition MyAlias'
-// @has - '//*[@class="sidebar"]//a[@href="#methods"]' 'Methods'
-// @has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Trait Implementations'
+// @has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Methods'
+// @has - '//*[@class="sidebar"]//a[@href="#trait-implementations"]' 'Trait Implementations'
 /// Alias docstring
 pub type MyAlias = MyStruct;
 
diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.rs b/src/test/ui/consts/const-eval/ub-wide-ptr.rs
index 0200bfe9f08..29ac32fcf22 100644
--- a/src/test/ui/consts/const-eval/ub-wide-ptr.rs
+++ b/src/test/ui/consts/const-eval/ub-wide-ptr.rs
@@ -104,6 +104,14 @@ const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u
 // bad trait object
 const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) };
 //~^ ERROR it is undefined behavior to use this value
+const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
+//~^ ERROR it is undefined behavior to use this value
+const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) };
+//~^ ERROR it is undefined behavior to use this value
+const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) };
+//~^ ERROR it is undefined behavior to use this value
+const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: &dyn Trait = unsafe { mem::transmute((&92u8, &[&42u8; 8])) };
+//~^ ERROR it is undefined behavior to use this value
 
 // bad data *inside* the trait object
 const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) };
diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.stderr
index e56459a7bde..94ca60596d6 100644
--- a/src/test/ui/consts/const-eval/ub-wide-ptr.stderr
+++ b/src/test/ui/consts/const-eval/ub-wide-ptr.stderr
@@ -138,7 +138,7 @@ error[E0080]: it is undefined behavior to use this value
   --> $DIR/ub-wide-ptr.rs:99:1
    |
 LL | const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { mem::transmute((&92u8, &3u8)) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
 
@@ -146,7 +146,7 @@ error[E0080]: it is undefined behavior to use this value
   --> $DIR/ub-wide-ptr.rs:102:1
    |
 LL | const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
 
@@ -154,46 +154,78 @@ error[E0080]: it is undefined behavior to use this value
   --> $DIR/ub-wide-ptr.rs:105:1
    |
 LL | const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/ub-wide-ptr.rs:107:1
+   |
+LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned vtable pointer in wide pointer
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
 
 error[E0080]: it is undefined behavior to use this value
   --> $DIR/ub-wide-ptr.rs:109:1
    |
+LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) };
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/ub-wide-ptr.rs:111:1
+   |
+LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) };
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/ub-wide-ptr.rs:113:1
+   |
+LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: &dyn Trait = unsafe { mem::transmute((&92u8, &[&42u8; 8])) };
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/ub-wide-ptr.rs:117:1
+   |
 LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.<dyn-downcast>, but expected a boolean
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-wide-ptr.rs:113:1
+  --> $DIR/ub-wide-ptr.rs:121:1
    |
 LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-wide-ptr.rs:115:1
+  --> $DIR/ub-wide-ptr.rs:123:1
    |
 LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
 
 error[E0080]: could not evaluate static initializer
-  --> $DIR/ub-wide-ptr.rs:121:5
+  --> $DIR/ub-wide-ptr.rs:129:5
    |
 LL |     mem::transmute::<_, &dyn Trait>((&92u8, 0usize))
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inbounds test failed: 0x0 is not a valid pointer
 
 error[E0080]: could not evaluate static initializer
-  --> $DIR/ub-wide-ptr.rs:125:5
+  --> $DIR/ub-wide-ptr.rs:133:5
    |
 LL |     mem::transmute::<_, &dyn Trait>((&92u8, &3u64))
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N
 
-error: aborting due to 24 previous errors
+error: aborting due to 28 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/generic-associated-types/construct_with_other_type.stderr b/src/test/ui/generic-associated-types/construct_with_other_type.stderr
index bad746f7ef1..b9468b3330b 100644
--- a/src/test/ui/generic-associated-types/construct_with_other_type.stderr
+++ b/src/test/ui/generic-associated-types/construct_with_other_type.stderr
@@ -2,11 +2,16 @@ error[E0271]: type mismatch resolving `for<'a> <<T as Baz>::Baa<'a> as std::ops:
   --> $DIR/construct_with_other_type.rs:19:9
    |
 LL | impl<T> Baz for T where T: Foo {
-   |         ^^^ expected type parameter `T`, found associated type
+   |      -  ^^^ expected type parameter `T`, found associated type
+   |      |
+   |      this type parameter
    |
    = note: expected associated type `<T as Foo>::Bar<'_, 'static>`
               found associated type `<<T as Baz>::Quux<'_> as Foo>::Bar<'_, 'static>`
-   = note: you might be missing a type parameter or trait bound
+help: consider further restricting this bound
+   |
+LL | impl<T> Baz for T where T: Foo + Baz<Quux = T> {
+   |                                ^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/generic-associated-types/missing-bounds.fixed b/src/test/ui/generic-associated-types/missing-bounds.fixed
new file mode 100644
index 00000000000..364d2388741
--- /dev/null
+++ b/src/test/ui/generic-associated-types/missing-bounds.fixed
@@ -0,0 +1,46 @@
+// run-rustfix
+
+use std::ops::Add;
+
+struct A<B>(B);
+
+impl<B> Add for A<B> where B: Add + std::ops::Add<Output = B> {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        A(self.0 + rhs.0) //~ ERROR mismatched types
+    }
+}
+
+struct C<B>(B);
+
+impl<B: Add + std::ops::Add<Output = B>> Add for C<B> {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        Self(self.0 + rhs.0) //~ ERROR mismatched types
+    }
+}
+
+struct D<B>(B);
+
+impl<B: std::ops::Add<Output = B>> Add for D<B> {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B`
+    }
+}
+
+struct E<B>(B);
+
+impl<B: Add> Add for E<B> where B: Add<Output = B>, B: std::ops::Add<Output = B> {
+    //~^ ERROR equality constraints are not yet supported in `where` clauses
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        Self(self.0 + rhs.0) //~ ERROR mismatched types
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/missing-bounds.rs b/src/test/ui/generic-associated-types/missing-bounds.rs
new file mode 100644
index 00000000000..ffafff5e9f5
--- /dev/null
+++ b/src/test/ui/generic-associated-types/missing-bounds.rs
@@ -0,0 +1,46 @@
+// run-rustfix
+
+use std::ops::Add;
+
+struct A<B>(B);
+
+impl<B> Add for A<B> where B: Add {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        A(self.0 + rhs.0) //~ ERROR mismatched types
+    }
+}
+
+struct C<B>(B);
+
+impl<B: Add> Add for C<B> {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        Self(self.0 + rhs.0) //~ ERROR mismatched types
+    }
+}
+
+struct D<B>(B);
+
+impl<B> Add for D<B> {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B`
+    }
+}
+
+struct E<B>(B);
+
+impl<B: Add> Add for E<B> where <B as Add>::Output = B {
+    //~^ ERROR equality constraints are not yet supported in `where` clauses
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        Self(self.0 + rhs.0) //~ ERROR mismatched types
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/missing-bounds.stderr b/src/test/ui/generic-associated-types/missing-bounds.stderr
new file mode 100644
index 00000000000..50536fdaca9
--- /dev/null
+++ b/src/test/ui/generic-associated-types/missing-bounds.stderr
@@ -0,0 +1,77 @@
+error: equality constraints are not yet supported in `where` clauses
+  --> $DIR/missing-bounds.rs:37:33
+   |
+LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^ not supported
+   |
+   = note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
+help: if `Output` is an associated type you're trying to set, use the associated type binding syntax
+   |
+LL | impl<B: Add> Add for E<B> where B: Add<Output = B> {
+   |                                 ^^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/missing-bounds.rs:11:11
+   |
+LL | impl<B> Add for A<B> where B: Add {
+   |      - this type parameter
+...
+LL |         A(self.0 + rhs.0)
+   |           ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type
+   |
+   = note: expected type parameter `B`
+             found associated type `<B as std::ops::Add>::Output`
+help: consider further restricting this bound
+   |
+LL | impl<B> Add for A<B> where B: Add + std::ops::Add<Output = B> {
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/missing-bounds.rs:21:14
+   |
+LL | impl<B: Add> Add for C<B> {
+   |      - this type parameter
+...
+LL |         Self(self.0 + rhs.0)
+   |              ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type
+   |
+   = note: expected type parameter `B`
+             found associated type `<B as std::ops::Add>::Output`
+help: consider further restricting this bound
+   |
+LL | impl<B: Add + std::ops::Add<Output = B>> Add for C<B> {
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0369]: cannot add `B` to `B`
+  --> $DIR/missing-bounds.rs:31:21
+   |
+LL |         Self(self.0 + rhs.0)
+   |              ------ ^ ----- B
+   |              |
+   |              B
+   |
+help: consider restricting type parameter `B`
+   |
+LL | impl<B: std::ops::Add<Output = B>> Add for D<B> {
+   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/missing-bounds.rs:42:14
+   |
+LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
+   |      - this type parameter
+...
+LL |         Self(self.0 + rhs.0)
+   |              ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type
+   |
+   = note: expected type parameter `B`
+             found associated type `<B as std::ops::Add>::Output`
+help: consider further restricting type parameter `B`
+   |
+LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B, B: std::ops::Add<Output = B> {
+   |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0308, E0369.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/issues/issue-20005.stderr b/src/test/ui/issues/issue-20005.stderr
index 19ccf707619..f53489a99f3 100644
--- a/src/test/ui/issues/issue-20005.stderr
+++ b/src/test/ui/issues/issue-20005.stderr
@@ -5,12 +5,18 @@ LL | trait From<Src> {
    |            --- required by this bound in `From`
 ...
 LL |     ) -> <Dst as From<Self>>::Result where Dst: From<Self> {
-   |                                                 ^^^^^^^^^^- help: consider further restricting `Self`: `, Self: std::marker::Sized`
-   |                                                 |
-   |                                                 doesn't have a size known at compile-time
+   |                                                 ^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `std::marker::Sized` is not implemented for `Self`
    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+help: consider further restricting `Self`
+   |
+LL |     ) -> <Dst as From<Self>>::Result where Dst: From<Self>, Self: std::marker::Sized  {
+   |                                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL | trait From<Src: ?Sized> {
+   |               ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-24204.stderr b/src/test/ui/issues/issue-24204.stderr
index d69efc86005..d5cbcf786bf 100644
--- a/src/test/ui/issues/issue-24204.stderr
+++ b/src/test/ui/issues/issue-24204.stderr
@@ -7,7 +7,9 @@ LL |     type A: MultiDispatch<Self::B, O = Self>;
    |                                    -------- required by this bound in `Trait`
 ...
 LL | fn test<T: Trait<B=i32>>(b: i32) -> T where T::A: MultiDispatch<i32> { T::new(b) }
-   |            ^^^^^^^^^^^^ expected type parameter `T`, found associated type
+   |         -  ^^^^^^^^^^^^ expected type parameter `T`, found associated type
+   |         |
+   |         this type parameter
    |
    = note: expected type parameter `T`
              found associated type `<<T as Trait>::A as MultiDispatch<i32>>::O`
diff --git a/src/test/ui/issues/issue-6738.stderr b/src/test/ui/issues/issue-6738.stderr
index 82b670bd03b..a428ff7e91f 100644
--- a/src/test/ui/issues/issue-6738.stderr
+++ b/src/test/ui/issues/issue-6738.stderr
@@ -6,7 +6,10 @@ LL |         self.x += v.x;
    |         |
    |         cannot use `+=` on type `T`
    |
-   = note: `T` might need a bound for `std::ops::AddAssign`
+help: consider restricting type parameter `T`
+   |
+LL | impl<T: std::ops::AddAssign> Foo<T> {
+   |       ^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/type/type-check/missing_trait_impl.stderr b/src/test/ui/type/type-check/missing_trait_impl.stderr
index 7186d6a542d..30df1261cef 100644
--- a/src/test/ui/type/type-check/missing_trait_impl.stderr
+++ b/src/test/ui/type/type-check/missing_trait_impl.stderr
@@ -6,7 +6,10 @@ LL |     let z = x + y;
    |             |
    |             T
    |
-   = note: `T` might need a bound for `std::ops::Add`
+help: consider restricting type parameter `T`
+   |
+LL | fn foo<T: std::ops::Add<Output = T>>(x: T, y: T) {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0368]: binary assignment operation `+=` cannot be applied to type `T`
   --> $DIR/missing_trait_impl.rs:9:5
@@ -16,7 +19,10 @@ LL |     x += x;
    |     |
    |     cannot use `+=` on type `T`
    |
-   = note: `T` might need a bound for `std::ops::AddAssign`
+help: consider restricting type parameter `T`
+   |
+LL | fn bar<T: std::ops::AddAssign>(x: T) {
+   |         ^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 2 previous errors