about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs19
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs74
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs87
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs60
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs40
-rw-r--r--compiler/rustc_infer/src/infer/at.rs25
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs28
-rw-r--r--compiler/rustc_middle/src/hir/mod.rs10
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs11
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs71
-rw-r--r--compiler/rustc_middle/src/query/mod.rs8
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs2
-rw-r--r--compiler/rustc_middle/src/traits/select.rs15
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs7
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs28
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs200
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs1035
-rw-r--r--compiler/rustc_mir_transform/src/deaggregator.rs4
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_drops.rs2
-rw-r--r--compiler/rustc_mir_transform/src/generator.rs2
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs10
-rw-r--r--compiler/rustc_mir_transform/src/pass_manager.rs2
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs25
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs29
-rw-r--r--compiler/rustc_resolve/src/imports.rs97
-rw-r--r--compiler/rustc_resolve/src/late.rs138
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs40
-rw-r--r--compiler/rustc_resolve/src/lib.rs630
-rw-r--r--compiler/rustc_resolve/src/macros.rs56
-rw-r--r--compiler/rustc_span/src/symbol.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs87
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs187
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs9
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs24
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs20
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs20
-rw-r--r--compiler/rustc_typeck/src/check/dropck.rs2
-rw-r--r--library/core/src/num/uint_macros.rs1
-rw-r--r--library/core/src/str/mod.rs4
-rw-r--r--src/bootstrap/native.rs2
-rw-r--r--src/librustdoc/doctest.rs10
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs28
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links/early.rs27
-rw-r--r--src/test/codegen/consts.rs2
-rw-r--r--src/test/debuginfo/unsized.rs19
-rw-r--r--src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs4
-rw-r--r--src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr13
-rw-r--r--src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs4
-rw-r--r--src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr13
-rw-r--r--src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs4
-rw-r--r--src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr13
-rw-r--r--src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs25
-rw-r--r--src/test/ui/coherence/coherence-negative-inherent.rs22
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs26
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr11
-rw-r--r--src/test/ui/const-generics/issues/issue-83765.rs129
-rw-r--r--src/test/ui/const-generics/issues/issue-83765.stderr137
-rw-r--r--src/test/ui/const_prop/inline_spans.rs9
-rw-r--r--src/test/ui/const_prop/inline_spans.stderr13
-rw-r--r--src/test/ui/consts/const-eval/const-eval-query-stack.stderr5
-rw-r--r--src/test/ui/consts/const-eval/issue-49296.stderr2
-rw-r--r--src/test/ui/consts/const-eval/panic-assoc-never-type.rs1
-rw-r--r--src/test/ui/consts/const-eval/panic-assoc-never-type.stderr8
-rw-r--r--src/test/ui/consts/const-eval/promoted_errors.noopt.stderr48
-rw-r--r--src/test/ui/consts/const-eval/promoted_errors.opt.stderr52
-rw-r--r--src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr48
-rw-r--r--src/test/ui/consts/const-eval/promoted_errors.rs5
-rw-r--r--src/test/ui/consts/const-eval/union-const-eval-field.rs1
-rw-r--r--src/test/ui/consts/const-eval/union-const-eval-field.stderr8
-rw-r--r--src/test/ui/consts/issue-56164.rs2
-rw-r--r--src/test/ui/consts/issue-56164.stderr14
-rw-r--r--src/test/ui/consts/issue-66693.rs5
-rw-r--r--src/test/ui/consts/issue-66693.stderr12
-rw-r--r--src/test/ui/consts/promotion.rs6
-rw-r--r--src/test/ui/recursion/issue-26548-recursion-via-normalize.rs2
-rw-r--r--src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr6
-rw-r--r--src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed10
-rw-r--r--src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs10
-rw-r--r--src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr55
-rw-r--r--src/test/ui/rust-2018/edition-lint-nested-paths.fixed6
-rw-r--r--src/test/ui/rust-2018/edition-lint-nested-paths.rs6
-rw-r--r--src/test/ui/rust-2018/edition-lint-nested-paths.stderr33
-rw-r--r--src/test/ui/rust-2018/edition-lint-paths.fixed6
-rw-r--r--src/test/ui/rust-2018/edition-lint-paths.rs6
-rw-r--r--src/test/ui/rust-2018/edition-lint-paths.stderr45
-rw-r--r--src/test/ui/rust-2018/extern-crate-rename.fixed2
-rw-r--r--src/test/ui/rust-2018/extern-crate-rename.rs2
-rw-r--r--src/test/ui/rust-2018/extern-crate-rename.stderr11
-rw-r--r--src/test/ui/rust-2018/extern-crate-submod.fixed3
-rw-r--r--src/test/ui/rust-2018/extern-crate-submod.rs3
-rw-r--r--src/test/ui/rust-2018/extern-crate-submod.stderr11
-rw-r--r--src/test/ui/typeck/issue-90319.rs17
-rw-r--r--src/test/ui/typeck/issue-90319.stderr9
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml2
-rw-r--r--src/tools/clippy/CHANGELOG.md3
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_enum_constructor.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/collapsible_if.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_all.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_complexity.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_lints.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_nursery.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_perf.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_restriction.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_style.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs396
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_flatten.rs118
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs81
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs68
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs41
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs46
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/single_component_path_imports.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/mod.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs98
-rw-r--r--src/tools/clippy/clippy_lints/src/try_err.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs9
-rw-r--r--src/tools/clippy/clippy_utils/src/diagnostics.rs86
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs2
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/tests/ui/cast_enum_constructor.rs17
-rw-r--r--src/tools/clippy/tests/ui/cast_enum_constructor.stderr16
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5497.stderr10
-rw-r--r--src/tools/clippy/tests/ui/map_flatten.rs74
-rw-r--r--src/tools/clippy/tests/ui/map_flatten.stderr125
-rw-r--r--src/tools/clippy/tests/ui/map_flatten_fixable.fixed (renamed from src/tools/clippy/tests/ui/map_flatten.fixed)0
-rw-r--r--src/tools/clippy/tests/ui/map_flatten_fixable.rs31
-rw-r--r--src/tools/clippy/tests/ui/map_flatten_fixable.stderr80
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms.stderr165
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms2.rs53
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms2.stderr228
-rw-r--r--src/tools/clippy/tests/ui/or_then_unwrap.fixed52
-rw-r--r--src/tools/clippy/tests/ui/or_then_unwrap.rs52
-rw-r--r--src/tools/clippy/tests/ui/or_then_unwrap.stderr22
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.rs7
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports_macro.fixed20
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports_macro.rs4
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports_macro.stderr10
-rw-r--r--src/tools/clippy/tests/ui/transmute_undefined_repr.rs55
-rw-r--r--src/tools/clippy/tests/ui/transmute_undefined_repr.stderr34
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr4
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_join.fixed35
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_join.rs37
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_join.stderr20
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed8
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs8
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr145
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr12
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.fixed48
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.rs48
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.stderr8
163 files changed, 4503 insertions, 2117 deletions
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index f02c7b2d2e1..74e194750fa 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -166,6 +166,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
     pointee_type: Ty<'tcx>,
     unique_type_id: UniqueTypeId<'tcx>,
 ) -> DINodeCreationResult<'ll> {
+    // The debuginfo generated by this function is only valid if `ptr_type` is really just
+    // a (fat) pointer. Make sure it is not called for e.g. `Box<T, NonZSTAllocator>`.
+    debug_assert_eq!(
+        cx.size_and_align_of(ptr_type),
+        cx.size_and_align_of(cx.tcx.mk_mut_ptr(pointee_type))
+    );
+
     let pointee_type_di_node = type_di_node(cx, pointee_type);
 
     return_if_di_node_created_in_meantime!(cx, unique_type_id);
@@ -212,7 +219,17 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
                     DIFlags::FlagZero,
                 ),
                 |cx, owner| {
-                    let layout = cx.layout_of(ptr_type);
+                    // FIXME: If this fat pointer is a `Box` then we don't want to use its
+                    //        type layout and instead use the layout of the raw pointer inside
+                    //        of it.
+                    //        The proper way to handle this is to not treat Box as a pointer
+                    //        at all and instead emit regular struct debuginfo for it. We just
+                    //        need to make sure that we don't break existing debuginfo consumers
+                    //        by doing that (at least not without a warning period).
+                    let layout_type =
+                        if ptr_type.is_box() { cx.tcx.mk_mut_ptr(pointee_type) } else { ptr_type };
+
+                    let layout = cx.layout_of(layout_type);
                     let addr_field = layout.field(cx, abi::FAT_PTR_ADDR);
                     let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA);
 
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 30764f689c9..faea2111d92 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -42,7 +42,7 @@ pub struct PromoteTemps<'tcx> {
 
 impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
     fn phase_change(&self) -> Option<MirPhase> {
-        Some(MirPhase::ConstPromotion)
+        Some(MirPhase::ConstsPromoted)
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index cf15fc4ddc3..deeca78b75d 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -266,22 +266,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             );
                         }
                     }
-                    // The deaggregator currently does not deaggreagate arrays.
-                    // So for now, we ignore them here.
-                    Rvalue::Aggregate(box AggregateKind::Array { .. }, _) => {}
-                    // All other aggregates must be gone after some phases.
-                    Rvalue::Aggregate(box kind, _) => {
-                        if self.mir_phase > MirPhase::DropLowering
-                            && !matches!(kind, AggregateKind::Generator(..))
-                        {
-                            // Generators persist until the state machine transformation, but all
-                            // other aggregates must have been lowered.
-                            self.fail(
-                                location,
-                                format!("{:?} have been lowered to field assignments", rvalue),
-                            )
-                        } else if self.mir_phase > MirPhase::GeneratorLowering {
-                            // No more aggregates after drop and generator lowering.
+                    Rvalue::Aggregate(agg_kind, _) => {
+                        let disallowed = match **agg_kind {
+                            AggregateKind::Array(..) => false,
+                            AggregateKind::Generator(..) => {
+                                self.mir_phase >= MirPhase::GeneratorsLowered
+                            }
+                            _ => self.mir_phase >= MirPhase::Deaggregated,
+                        };
+                        if disallowed {
                             self.fail(
                                 location,
                                 format!("{:?} have been lowered to field assignments", rvalue),
@@ -289,7 +282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         }
                     }
                     Rvalue::Ref(_, BorrowKind::Shallow, _) => {
-                        if self.mir_phase > MirPhase::DropLowering {
+                        if self.mir_phase >= MirPhase::DropsLowered {
                             self.fail(
                                 location,
                                 "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
@@ -300,7 +293,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 }
             }
             StatementKind::AscribeUserType(..) => {
-                if self.mir_phase > MirPhase::DropLowering {
+                if self.mir_phase >= MirPhase::DropsLowered {
                     self.fail(
                         location,
                         "`AscribeUserType` should have been removed after drop lowering phase",
@@ -308,7 +301,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 }
             }
             StatementKind::FakeRead(..) => {
-                if self.mir_phase > MirPhase::DropLowering {
+                if self.mir_phase >= MirPhase::DropsLowered {
                     self.fail(
                         location,
                         "`FakeRead` should have been removed after drop lowering phase",
@@ -351,10 +344,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty))
                 }
             }
-            StatementKind::SetDiscriminant { .. }
-            | StatementKind::StorageLive(..)
+            StatementKind::SetDiscriminant { .. } => {
+                if self.mir_phase < MirPhase::DropsLowered {
+                    self.fail(location, "`SetDiscriminant` is not allowed until drop elaboration");
+                }
+            }
+            StatementKind::Retag(_, _) => {
+                // FIXME(JakobDegen) The validator should check that `self.mir_phase <
+                // DropsLowered`. However, this causes ICEs with generation of drop shims, which
+                // seem to fail to set their `MirPhase` correctly.
+            }
+            StatementKind::StorageLive(..)
             | StatementKind::StorageDead(..)
-            | StatementKind::Retag(_, _)
             | StatementKind::Coverage(_)
             | StatementKind::Nop => {}
         }
@@ -424,10 +425,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 }
             }
             TerminatorKind::DropAndReplace { target, unwind, .. } => {
-                if self.mir_phase > MirPhase::DropLowering {
+                if self.mir_phase >= MirPhase::DropsLowered {
                     self.fail(
                         location,
-                        "`DropAndReplace` is not permitted to exist after drop elaboration",
+                        "`DropAndReplace` should have been removed during drop elaboration",
                     );
                 }
                 self.check_edge(location, *target, EdgeKind::Normal);
@@ -494,7 +495,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 }
             }
             TerminatorKind::Yield { resume, drop, .. } => {
-                if self.mir_phase > MirPhase::GeneratorLowering {
+                if self.mir_phase >= MirPhase::GeneratorsLowered {
                     self.fail(location, "`Yield` should have been replaced by generator lowering");
                 }
                 self.check_edge(location, *resume, EdgeKind::Normal);
@@ -503,10 +504,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 }
             }
             TerminatorKind::FalseEdge { real_target, imaginary_target } => {
+                if self.mir_phase >= MirPhase::DropsLowered {
+                    self.fail(
+                        location,
+                        "`FalseEdge` should have been removed after drop elaboration",
+                    );
+                }
                 self.check_edge(location, *real_target, EdgeKind::Normal);
                 self.check_edge(location, *imaginary_target, EdgeKind::Normal);
             }
             TerminatorKind::FalseUnwind { real_target, unwind } => {
+                if self.mir_phase >= MirPhase::DropsLowered {
+                    self.fail(
+                        location,
+                        "`FalseUnwind` should have been removed after drop elaboration",
+                    );
+                }
                 self.check_edge(location, *real_target, EdgeKind::Normal);
                 if let Some(unwind) = unwind {
                     self.check_edge(location, *unwind, EdgeKind::Unwind);
@@ -520,12 +533,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     self.check_edge(location, *cleanup, EdgeKind::Unwind);
                 }
             }
+            TerminatorKind::GeneratorDrop => {
+                if self.mir_phase >= MirPhase::GeneratorsLowered {
+                    self.fail(
+                        location,
+                        "`GeneratorDrop` should have been replaced by generator lowering",
+                    );
+                }
+            }
             // Nothing to validate for these.
             TerminatorKind::Resume
             | TerminatorKind::Abort
             | TerminatorKind::Return
-            | TerminatorKind::Unreachable
-            | TerminatorKind::GeneratorDrop => {}
+            | TerminatorKind::Unreachable => {}
         }
 
         self.super_terminator(terminator, location);
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index d8071bf159a..5e97fc90320 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -101,7 +101,14 @@ struct MatcherTtFrame<'tt> {
     idx: usize,
 }
 
-type NamedMatchVec = SmallVec<[NamedMatch; 4]>;
+// One element is enough to cover 95-99% of vectors for most benchmarks. Also,
+// vectors longer than one frequently have many elements, not just two or
+// three.
+type NamedMatchVec = SmallVec<[NamedMatch; 1]>;
+
+// This type is used a lot. Make sure it doesn't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(NamedMatchVec, 48);
 
 /// Represents a single "position" (aka "matcher position", aka "item"), as
 /// described in the module documentation.
@@ -153,7 +160,7 @@ struct MatcherPos<'tt> {
 
 // This type is used a lot. Make sure it doesn't unintentionally get bigger.
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(MatcherPos<'_>, 136);
+rustc_data_structures::static_assert_size!(MatcherPos<'_>, 112);
 
 impl<'tt> MatcherPos<'tt> {
     /// `len` `Vec`s (initially shared and empty) that will store matches of metavars.
@@ -202,11 +209,7 @@ impl<'tt> MatcherPos<'tt> {
             match_lo: up.match_cur,
             match_cur: up.match_cur,
             match_hi: up.match_cur + seq.num_captures,
-            repetition: Some(MatcherPosRepetition {
-                up,
-                sep: seq.separator.clone(),
-                seq_op: seq.kleene.op,
-            }),
+            repetition: Some(MatcherPosRepetition { up, seq }),
             stack: smallvec![],
         }
     }
@@ -220,15 +223,12 @@ impl<'tt> MatcherPos<'tt> {
 
 #[derive(Clone)]
 struct MatcherPosRepetition<'tt> {
-    /// The KleeneOp of this sequence.
-    seq_op: mbe::KleeneOp,
-
-    /// The separator.
-    sep: Option<Token>,
-
     /// The "parent" matcher position. That is, the matcher position just before we enter the
     /// sequence.
     up: Box<MatcherPos<'tt>>,
+
+    /// The sequence itself.
+    seq: &'tt SequenceRepetition,
 }
 
 enum EofItems<'tt> {
@@ -274,22 +274,20 @@ pub(super) fn count_names(ms: &[TokenTree]) -> usize {
     })
 }
 
-/// `NamedMatch` is a pattern-match result for a single `token::MATCH_NONTERMINAL`:
-/// so it is associated with a single ident in a parse, and all
-/// `MatchedNonterminal`s in the `NamedMatch` have the same non-terminal type
-/// (expr, item, etc). Each leaf in a single `NamedMatch` corresponds to a
-/// single `token::MATCH_NONTERMINAL` in the `TokenTree` that produced it.
+/// `NamedMatch` is a pattern-match result for a single metavar. All
+/// `MatchedNtNonTt`s in the `NamedMatch` have the same non-terminal type
+/// (expr, item, etc).
 ///
 /// The in-memory structure of a particular `NamedMatch` represents the match
 /// that occurred when a particular subset of a matcher was applied to a
 /// particular token tree.
 ///
 /// The width of each `MatchedSeq` in the `NamedMatch`, and the identity of
-/// the `MatchedNonterminal`s, will depend on the token tree it was applied
-/// to: each `MatchedSeq` corresponds to a single `TTSeq` in the originating
+/// the `MatchedNtNonTts`s, will depend on the token tree it was applied
+/// to: each `MatchedSeq` corresponds to a single repetition in the originating
 /// token tree. The depth of the `NamedMatch` structure will therefore depend
-/// only on the nesting depth of `ast::TTSeq`s in the originating
-/// token tree it was derived from.
+/// only on the nesting depth of repetitions in the originating token tree it
+/// was derived from.
 ///
 /// In layman's terms: `NamedMatch` will form a tree representing nested matches of a particular
 /// meta variable. For example, if we are matching the following macro against the following
@@ -308,24 +306,32 @@ pub(super) fn count_names(ms: &[TokenTree]) -> usize {
 /// ```rust
 /// MatchedSeq([
 ///   MatchedSeq([
-///     MatchedNonterminal(a),
-///     MatchedNonterminal(b),
-///     MatchedNonterminal(c),
-///     MatchedNonterminal(d),
+///     MatchedNtNonTt(a),
+///     MatchedNtNonTt(b),
+///     MatchedNtNonTt(c),
+///     MatchedNtNonTt(d),
 ///   ]),
 ///   MatchedSeq([
-///     MatchedNonterminal(a),
-///     MatchedNonterminal(b),
-///     MatchedNonterminal(c),
-///     MatchedNonterminal(d),
-///     MatchedNonterminal(e),
+///     MatchedNtNonTt(a),
+///     MatchedNtNonTt(b),
+///     MatchedNtNonTt(c),
+///     MatchedNtNonTt(d),
+///     MatchedNtNonTt(e),
 ///   ])
 /// ])
 /// ```
 #[derive(Debug, Clone)]
 crate enum NamedMatch {
     MatchedSeq(Lrc<NamedMatchVec>),
-    MatchedNonterminal(Lrc<Nonterminal>),
+
+    // This variant should never hold an `NtTT`. `MatchedNtTt` should be used
+    // for that case.
+    MatchedNtNonTt(Lrc<Nonterminal>),
+
+    // `NtTT` is handled without any cloning when transcribing, unlike other
+    // nonterminals. Therefore, an `Lrc` isn't helpful and causes unnecessary
+    // allocations. Hence this separate variant.
+    MatchedNtTt(rustc_ast::tokenstream::TokenTree),
 }
 
 /// Takes a slice of token trees `ms` representing a matcher which successfully matched input
@@ -546,14 +552,19 @@ impl<'tt> TtParser<'tt> {
                     self.cur_items.push(new_pos);
                 }
 
-                if idx == len && repetition.sep.is_some() {
-                    if repetition.sep.as_ref().map_or(false, |sep| token_name_eq(token, sep)) {
+                if idx == len && repetition.seq.separator.is_some() {
+                    if repetition
+                        .seq
+                        .separator
+                        .as_ref()
+                        .map_or(false, |sep| token_name_eq(token, sep))
+                    {
                         // The matcher has a separator, and it matches the current token. We can
                         // advance past the separator token.
                         item.idx += 1;
                         self.next_items.push(item);
                     }
-                } else if repetition.seq_op != mbe::KleeneOp::ZeroOrOne {
+                } else if repetition.seq.kleene.op != mbe::KleeneOp::ZeroOrOne {
                     // We don't need a separator. Move the "dot" back to the beginning of the
                     // matcher and try to match again UNLESS we are only allowed to have _one_
                     // repetition.
@@ -665,7 +676,11 @@ impl<'tt> TtParser<'tt> {
                             }
                             Ok(nt) => nt,
                         };
-                        item.push_match(match_cur, MatchedNonterminal(Lrc::new(nt)));
+                        let m = match nt {
+                            Nonterminal::NtTT(tt) => MatchedNtTt(tt),
+                            _ => MatchedNtNonTt(Lrc::new(nt)),
+                        };
+                        item.push_match(match_cur, m);
                         item.idx += 1;
                         item.match_cur += 1;
                     } else {
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index f13b97251d2..7837de5c18d 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -4,11 +4,11 @@ use crate::expand::{ensure_complete_parse, parse_ast_fragment, AstFragment, AstF
 use crate::mbe;
 use crate::mbe::macro_check;
 use crate::mbe::macro_parser::{Error, ErrorReported, Failure, Success, TtParser};
-use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq};
+use crate::mbe::macro_parser::{MatchedNtTt, MatchedSeq};
 use crate::mbe::transcribe::transcribe;
 
 use rustc_ast as ast;
-use rustc_ast::token::{self, NonterminalKind, NtTT, Token, TokenKind::*};
+use rustc_ast::token::{self, NonterminalKind, Token, TokenKind::*};
 use rustc_ast::tokenstream::{DelimSpan, TokenStream};
 use rustc_ast::{NodeId, DUMMY_NODE_ID};
 use rustc_ast_pretty::pprust;
@@ -470,22 +470,20 @@ pub fn compile_declarative_macro(
         MatchedSeq(ref s) => s
             .iter()
             .map(|m| {
-                if let MatchedNonterminal(ref nt) = *m {
-                    if let NtTT(ref tt) = **nt {
-                        let mut tts = vec![];
-                        mbe::quoted::parse(
-                            tt.clone().into(),
-                            true,
-                            &sess.parse_sess,
-                            def.id,
-                            features,
-                            edition,
-                            &mut tts,
-                        );
-                        let tt = tts.pop().unwrap();
-                        valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def, &tt);
-                        return tt;
-                    }
+                if let MatchedNtTt(ref tt) = *m {
+                    let mut tts = vec![];
+                    mbe::quoted::parse(
+                        tt.clone().into(),
+                        true,
+                        &sess.parse_sess,
+                        def.id,
+                        features,
+                        edition,
+                        &mut tts,
+                    );
+                    let tt = tts.pop().unwrap();
+                    valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def, &tt);
+                    return tt;
                 }
                 sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
             })
@@ -497,20 +495,18 @@ pub fn compile_declarative_macro(
         MatchedSeq(ref s) => s
             .iter()
             .map(|m| {
-                if let MatchedNonterminal(ref nt) = *m {
-                    if let NtTT(ref tt) = **nt {
-                        let mut tts = vec![];
-                        mbe::quoted::parse(
-                            tt.clone().into(),
-                            false,
-                            &sess.parse_sess,
-                            def.id,
-                            features,
-                            edition,
-                            &mut tts,
-                        );
-                        return tts.pop().unwrap();
-                    }
+                if let MatchedNtTt(ref tt) = *m {
+                    let mut tts = vec![];
+                    mbe::quoted::parse(
+                        tt.clone().into(),
+                        false,
+                        &sess.parse_sess,
+                        def.id,
+                        features,
+                        edition,
+                        &mut tts,
+                    );
+                    return tts.pop().unwrap();
                 }
                 sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
             })
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index e097f9d9c02..228ed04548d 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -1,8 +1,8 @@
 use crate::base::ExtCtxt;
-use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
+use crate::mbe::macro_parser::{MatchedNtNonTt, MatchedNtTt, MatchedSeq, NamedMatch};
 use crate::mbe::{self, MetaVarExpr};
 use rustc_ast::mut_visit::{self, MutVisitor};
-use rustc_ast::token::{self, NtTT, Token, TokenKind};
+use rustc_ast::token::{self, Nonterminal, Token, TokenKind};
 use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
@@ -233,25 +233,29 @@ pub(super) fn transcribe<'a>(
                 // the meta-var.
                 let ident = MacroRulesNormalizedIdent::new(orignal_ident);
                 if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
-                    if let MatchedNonterminal(nt) = cur_matched {
-                        let token = if let NtTT(tt) = &**nt {
+                    match cur_matched {
+                        MatchedNtTt(ref tt) => {
                             // `tt`s are emitted into the output stream directly as "raw tokens",
                             // without wrapping them into groups.
-                            tt.clone()
-                        } else {
+                            let token = tt.clone();
+                            result.push(token.into());
+                        }
+                        MatchedNtNonTt(ref nt) => {
                             // Other variables are emitted into the output stream as groups with
                             // `Delimiter::None` to maintain parsing priorities.
                             // `Interpolated` is currently used for such groups in rustc parser.
+                            debug_assert!(!matches!(**nt, Nonterminal::NtTT(_)));
                             marker.visit_span(&mut sp);
-                            TokenTree::token(token::Interpolated(nt.clone()), sp)
-                        };
-                        result.push(token.into());
-                    } else {
-                        // We were unable to descend far enough. This is an error.
-                        return Err(cx.struct_span_err(
-                            sp, /* blame the macro writer */
-                            &format!("variable '{}' is still repeating at this depth", ident),
-                        ));
+                            let token = TokenTree::token(token::Interpolated(nt.clone()), sp);
+                            result.push(token.into());
+                        }
+                        MatchedSeq(..) => {
+                            // We were unable to descend far enough. This is an error.
+                            return Err(cx.struct_span_err(
+                                sp, /* blame the macro writer */
+                                &format!("variable '{}' is still repeating at this depth", ident),
+                            ));
+                        }
                     }
                 } else {
                     // If we aren't able to match the meta-var, we push it back into the result but
@@ -308,7 +312,7 @@ fn lookup_cur_matched<'a>(
         let mut matched = matched;
         for &(idx, _) in repeats {
             match matched {
-                MatchedNonterminal(_) => break,
+                MatchedNtTt(_) | MatchedNtNonTt(_) => break,
                 MatchedSeq(ref ads) => matched = ads.get(idx).unwrap(),
             }
         }
@@ -398,7 +402,7 @@ fn lockstep_iter_size(
             let name = MacroRulesNormalizedIdent::new(name);
             match lookup_cur_matched(name, interpolations, repeats) {
                 Some(matched) => match matched {
-                    MatchedNonterminal(_) => LockstepIterSize::Unconstrained,
+                    MatchedNtTt(_) | MatchedNtNonTt(_) => LockstepIterSize::Unconstrained,
                     MatchedSeq(ref ads) => LockstepIterSize::Constraint(ads.len(), name),
                 },
                 _ => LockstepIterSize::Unconstrained,
@@ -445,7 +449,7 @@ fn count_repetitions<'a>(
         sp: &DelimSpan,
     ) -> PResult<'a, usize> {
         match matched {
-            MatchedNonterminal(_) => {
+            MatchedNtTt(_) | MatchedNtNonTt(_) => {
                 if declared_lhs_depth == 0 {
                     return Err(cx.struct_span_err(
                         sp.entire(),
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index 94991fdb201..ff8082840e1 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -28,7 +28,7 @@
 use super::*;
 
 use rustc_middle::ty::relate::{Relate, TypeRelation};
-use rustc_middle::ty::Const;
+use rustc_middle::ty::{Const, ImplSubject};
 
 pub struct At<'a, 'tcx> {
     pub infcx: &'a InferCtxt<'a, 'tcx>,
@@ -272,6 +272,29 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
     }
 }
 
+impl<'tcx> ToTrace<'tcx> for ImplSubject<'tcx> {
+    fn to_trace(
+        tcx: TyCtxt<'tcx>,
+        cause: &ObligationCause<'tcx>,
+        a_is_expected: bool,
+        a: Self,
+        b: Self,
+    ) -> TypeTrace<'tcx> {
+        match (a, b) {
+            (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => {
+                ToTrace::to_trace(tcx, cause, a_is_expected, trait_ref_a, trait_ref_b)
+            }
+            (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => {
+                ToTrace::to_trace(tcx, cause, a_is_expected, ty_a, ty_b)
+            }
+            (ImplSubject::Trait(_), ImplSubject::Inherent(_))
+            | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => {
+                bug!("can not trace TraitRef and Ty");
+            }
+        }
+    }
+}
+
 impl<'tcx> ToTrace<'tcx> for Ty<'tcx> {
     fn to_trace(
         _: TyCtxt<'tcx>,
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 5697e73f73a..2886d921c70 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -20,8 +20,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
 use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
-use rustc_middle::mir::interpret::ErrorHandled;
-use rustc_middle::mir::interpret::EvalToConstValueResult;
+use rustc_middle::mir::interpret::{ErrorHandled, EvalToConstValueResult};
 use rustc_middle::traits::select;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
@@ -71,7 +70,6 @@ mod sub;
 pub mod type_variable;
 mod undo_log;
 
-use crate::infer::canonical::OriginalQueryValues;
 pub use rustc_middle::infer::unify_key;
 
 #[must_use]
@@ -687,15 +685,28 @@ pub struct CombinedSnapshot<'a, 'tcx> {
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// calls `tcx.try_unify_abstract_consts` after
     /// canonicalizing the consts.
+    #[instrument(skip(self), level = "debug")]
     pub fn try_unify_abstract_consts(
         &self,
         a: ty::Unevaluated<'tcx, ()>,
         b: ty::Unevaluated<'tcx, ()>,
+        param_env: ty::ParamEnv<'tcx>,
     ) -> bool {
-        let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default());
-        debug!("canonical consts: {:?}", &canonical.value);
+        // Reject any attempt to unify two unevaluated constants that contain inference
+        // variables, since inference variables in queries lead to ICEs.
+        if a.substs.has_infer_types_or_consts()
+            || b.substs.has_infer_types_or_consts()
+            || param_env.has_infer_types_or_consts()
+        {
+            debug!("a or b or param_env contain infer vars in its substs -> cannot unify");
+            return false;
+        }
+
+        let param_env_and = param_env.and((a, b));
+        let erased = self.tcx.erase_regions(param_env_and);
+        debug!("after erase_regions: {:?}", erased);
 
-        self.tcx.try_unify_abstract_consts(canonical.value)
+        self.tcx.try_unify_abstract_consts(erased)
     }
 
     pub fn is_in_snapshot(&self) -> bool {
@@ -1598,6 +1609,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     ///
     /// This handles inferences variables within both `param_env` and `substs` by
     /// performing the operation on their respective canonical forms.
+    #[instrument(skip(self), level = "debug")]
     pub fn const_eval_resolve(
         &self,
         param_env: ty::ParamEnv<'tcx>,
@@ -1605,15 +1617,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         span: Option<Span>,
     ) -> EvalToConstValueResult<'tcx> {
         let substs = self.resolve_vars_if_possible(unevaluated.substs);
+        debug!(?substs);
 
         // Postpone the evaluation of constants whose substs depend on inference
         // variables
         if substs.has_infer_types_or_consts() {
+            debug!("substs have infer types or consts: {:?}", substs);
             return Err(ErrorHandled::TooGeneric);
         }
 
         let param_env_erased = self.tcx.erase_regions(param_env);
         let substs_erased = self.tcx.erase_regions(substs);
+        debug!(?param_env_erased);
+        debug!(?substs_erased);
 
         let unevaluated = ty::Unevaluated {
             def: unevaluated.def,
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index 1053f0cefbe..a9e22d16ee0 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -7,10 +7,10 @@ pub mod nested_filter;
 pub mod place;
 
 use crate::ty::query::Providers;
-use crate::ty::TyCtxt;
+use crate::ty::{ImplSubject, TyCtxt};
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_hir::def_id::LocalDefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::*;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_span::DUMMY_SP;
@@ -54,6 +54,12 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn parent_module(self, id: HirId) -> LocalDefId {
         self.parent_module_from_def_id(id.owner)
     }
+
+    pub fn impl_subject(self, def_id: DefId) -> ImplSubject<'tcx> {
+        self.impl_trait_ref(def_id)
+            .map(ImplSubject::Trait)
+            .unwrap_or_else(|| ImplSubject::Inherent(self.type_of(def_id)))
+    }
 }
 
 pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index 8b0f92d23d7..7e5989b4112 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -1,6 +1,7 @@
 use super::{ErrorHandled, EvalToConstValueResult, GlobalId};
 
 use crate::mir;
+use crate::ty::fold::TypeFoldable;
 use crate::ty::subst::InternalSubsts;
 use crate::ty::{self, TyCtxt};
 use rustc_hir::def_id::DefId;
@@ -38,6 +39,16 @@ impl<'tcx> TyCtxt<'tcx> {
         ct: ty::Unevaluated<'tcx>,
         span: Option<Span>,
     ) -> EvalToConstValueResult<'tcx> {
+        // Cannot resolve `Unevaluated` constants that contain inference
+        // variables. We reject those here since `resolve_opt_const_arg`
+        // would fail otherwise.
+        //
+        // When trying to evaluate constants containing inference variables,
+        // use `Infcx::const_eval_resolve` instead.
+        if ct.substs.has_infer_types_or_consts() {
+            bug!("did not expect inference variables here");
+        }
+
         match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) {
             Ok(Some(instance)) => {
                 let cid = GlobalId { instance, promoted: ct.promoted };
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index a5468b3f4f2..ea71cff1616 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -127,14 +127,11 @@ pub trait MirPass<'tcx> {
 /// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the
 /// dialects forbid certain variants or values in certain phases.
 ///
-/// Note: Each phase's validation checks all invariants of the *previous* phases' dialects. A phase
-/// that changes the dialect documents what invariants must be upheld *after* that phase finishes.
-///
 /// Warning: ordering of variants is significant.
 #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
 #[derive(HashStable)]
 pub enum MirPhase {
-    Build = 0,
+    Built = 0,
     // FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query).
     // We used to have this for pre-miri MIR based const eval.
     Const = 1,
@@ -142,17 +139,32 @@ pub enum MirPhase {
     /// by creating a new MIR body per promoted element. After this phase (and thus the termination
     /// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir`
     /// query.
-    ConstPromotion = 2,
-    /// After this phase
-    /// * the only `AggregateKind`s allowed are `Array` and `Generator`,
-    /// * `DropAndReplace` is gone for good
-    /// * `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` terminator
-    ///   means that the auto-generated drop glue will be invoked.
-    DropLowering = 3,
-    /// After this phase, generators are explicit state machines (no more `Yield`).
-    /// `AggregateKind::Generator` is gone for good.
-    GeneratorLowering = 4,
-    Optimization = 5,
+    ConstsPromoted = 2,
+    /// Beginning with this phase, the following variants are disallowed:
+    /// * [`TerminatorKind::DropAndReplace`](terminator::TerminatorKind::DropAndReplace)
+    /// * [`TerminatorKind::FalseUnwind`](terminator::TerminatorKind::FalseUnwind)
+    /// * [`TerminatorKind::FalseEdge`](terminator::TerminatorKind::FalseEdge)
+    /// * [`StatementKind::FakeRead`]
+    /// * [`StatementKind::AscribeUserType`]
+    /// * [`Rvalue::Ref`] with `BorrowKind::Shallow`
+    ///
+    /// And the following variant is allowed:
+    /// * [`StatementKind::Retag`]
+    ///
+    /// Furthermore, `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop`
+    /// terminator means that the auto-generated drop glue will be invoked.
+    DropsLowered = 3,
+    /// Beginning with this phase, the following variant is disallowed:
+    /// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
+    ///
+    /// And the following variant is allowed:
+    /// * [`StatementKind::SetDiscriminant`]
+    Deaggregated = 4,
+    /// Beginning with this phase, the following variants are disallowed:
+    /// * [`TerminatorKind::Yield`](terminator::TerminatorKind::Yield)
+    /// * [`TerminatorKind::GeneratorDrop](terminator::TerminatorKind::GeneratorDrop)
+    GeneratorsLowered = 5,
+    Optimized = 6,
 }
 
 impl MirPhase {
@@ -311,7 +323,7 @@ impl<'tcx> Body<'tcx> {
         );
 
         let mut body = Body {
-            phase: MirPhase::Build,
+            phase: MirPhase::Built,
             source,
             basic_blocks,
             source_scopes,
@@ -346,7 +358,7 @@ impl<'tcx> Body<'tcx> {
     /// crate.
     pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
         let mut body = Body {
-            phase: MirPhase::Build,
+            phase: MirPhase::Built,
             source: MirSource::item(DefId::local(CRATE_DEF_INDEX)),
             basic_blocks,
             source_scopes: IndexVec::new(),
@@ -1541,9 +1553,16 @@ impl Statement<'_> {
     }
 }
 
+/// The various kinds of statements that can appear in MIR.
+///
+/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which
+/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions,
+/// causing an ICE if they are violated.
 #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)]
 pub enum StatementKind<'tcx> {
     /// Write the RHS Rvalue to the LHS Place.
+    ///
+    /// The LHS place may not overlap with any memory accessed on the RHS.
     Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>),
 
     /// This represents all the reading that a pattern match may do
@@ -1761,6 +1780,19 @@ static_assert_size!(Place<'_>, 16);
 pub enum ProjectionElem<V, T> {
     Deref,
     Field(Field, T),
+    /// Index into a slice/array.
+    ///
+    /// Note that this does not also dereference, and so it does not exactly correspond to slice
+    /// indexing in Rust. In other words, in the below Rust code:
+    ///
+    /// ```rust
+    /// let x = &[1, 2, 3, 4];
+    /// let i = 2;
+    /// x[i];
+    /// ```
+    ///
+    /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same
+    /// thing is true of the `ConstantIndex` and `Subslice` projections below.
     Index(V),
 
     /// These indices are generated by slice patterns. Easiest to explain
@@ -2223,6 +2255,11 @@ impl<'tcx> Operand<'tcx> {
 /// Rvalues
 
 #[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
+/// The various kinds of rvalues that can appear in MIR.
+///
+/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which
+/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions,
+/// causing an ICE if they are violated.
 pub enum Rvalue<'tcx> {
     /// x (either a move or copy, depending on type of x)
     Use(Operand<'tcx>),
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 7a82533218b..95260e9e917 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -331,12 +331,12 @@ rustc_queries! {
         }
     }
 
-    query try_unify_abstract_consts(key: (
-        ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>
-    )) -> bool {
+    query try_unify_abstract_consts(key:
+        ty::ParamEnvAnd<'tcx, (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>
+    )>) -> bool {
         desc {
             |tcx| "trying to unify the generic constants {} and {}",
-            tcx.def_path_str(key.0.def.did), tcx.def_path_str(key.1.def.did)
+            tcx.def_path_str(key.value.0.def.did), tcx.def_path_str(key.value.1.def.did)
         }
     }
 
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 3ed7836074b..d852531bc68 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -491,7 +491,7 @@ pub enum SelectionError<'tcx> {
     /// A given constant couldn't be evaluated.
     NotConstEvaluatable(NotConstEvaluatable),
     /// Exceeded the recursion depth during type projection.
-    Overflow,
+    Overflow(OverflowError),
     /// Signaling that an error has already been emitted, to avoid
     /// multiple errors being shown.
     ErrorReporting,
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index 56d42706f67..5297825a92f 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -5,6 +5,7 @@
 use self::EvaluationResult::*;
 
 use super::{SelectionError, SelectionResult};
+use rustc_errors::ErrorGuaranteed;
 
 use crate::ty;
 
@@ -264,14 +265,26 @@ impl EvaluationResult {
 /// Indicates that trait evaluation caused overflow and in which pass.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable)]
 pub enum OverflowError {
+    Error(ErrorGuaranteed),
     Canonical,
     ErrorReporting,
 }
 
+impl From<ErrorGuaranteed> for OverflowError {
+    fn from(e: ErrorGuaranteed) -> OverflowError {
+        OverflowError::Error(e)
+    }
+}
+
+TrivialTypeFoldableAndLiftImpls! {
+    OverflowError,
+}
+
 impl<'tcx> From<OverflowError> for SelectionError<'tcx> {
     fn from(overflow_error: OverflowError) -> SelectionError<'tcx> {
         match overflow_error {
-            OverflowError::Canonical => SelectionError::Overflow,
+            OverflowError::Error(e) => SelectionError::Overflow(OverflowError::Error(e)),
+            OverflowError::Canonical => SelectionError::Overflow(OverflowError::Canonical),
             OverflowError::ErrorReporting => SelectionError::ErrorReporting,
         }
     }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 3124cc5dba8..31db66dc242 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -44,6 +44,7 @@ use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::Span;
 use rustc_target::abi::Align;
 
+use std::fmt::Debug;
 use std::hash::Hash;
 use std::ops::ControlFlow;
 use std::{fmt, str};
@@ -172,6 +173,12 @@ pub struct ImplHeader<'tcx> {
     pub predicates: Vec<Predicate<'tcx>>,
 }
 
+#[derive(Copy, Clone, Debug, TypeFoldable)]
+pub enum ImplSubject<'tcx> {
+    Trait(TraitRef<'tcx>),
+    Inherent(Ty<'tcx>),
+}
+
 #[derive(
     Copy,
     Clone,
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 81ee7942c4d..d9b55563996 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -7,7 +7,7 @@
 use crate::mir::interpret::{get_slice_bytes, ConstValue, GlobalAlloc, Scalar};
 use crate::ty::error::{ExpectedFound, TypeError};
 use crate::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
-use crate::ty::{self, Term, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{self, ImplSubject, Term, Ty, TyCtxt, TypeFoldable};
 use rustc_hir as ast;
 use rustc_hir::def_id::DefId;
 use rustc_span::DUMMY_SP;
@@ -356,6 +356,30 @@ impl<'tcx> Relate<'tcx> for GeneratorWitness<'tcx> {
     }
 }
 
+impl<'tcx> Relate<'tcx> for ImplSubject<'tcx> {
+    #[inline]
+    fn relate<R: TypeRelation<'tcx>>(
+        relation: &mut R,
+        a: ImplSubject<'tcx>,
+        b: ImplSubject<'tcx>,
+    ) -> RelateResult<'tcx, ImplSubject<'tcx>> {
+        match (a, b) {
+            (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => {
+                let trait_ref = ty::TraitRef::relate(relation, trait_ref_a, trait_ref_b)?;
+                Ok(ImplSubject::Trait(trait_ref))
+            }
+            (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => {
+                let ty = Ty::relate(relation, ty_a, ty_b)?;
+                Ok(ImplSubject::Inherent(ty))
+            }
+            (ImplSubject::Trait(_), ImplSubject::Inherent(_))
+            | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => {
+                bug!("can not relate TraitRef and Ty");
+            }
+        }
+    }
+}
+
 impl<'tcx> Relate<'tcx> for Ty<'tcx> {
     #[inline]
     fn relate<R: TypeRelation<'tcx>>(
@@ -585,7 +609,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
         (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu))
             if tcx.features().generic_const_exprs =>
         {
-            tcx.try_unify_abstract_consts((au.shrink(), bu.shrink()))
+            tcx.try_unify_abstract_consts(relation.param_env().and((au.shrink(), bu.shrink())))
         }
 
         // While this is slightly incorrect, it shouldn't matter for `min_const_generics`
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index c4d15d4d187..4f5fc38917f 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -6,30 +6,25 @@ use std::cell::Cell;
 use rustc_ast::Mutability;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::DefKind;
-use rustc_hir::HirId;
 use rustc_index::bit_set::BitSet;
 use rustc_index::vec::IndexVec;
 use rustc_middle::mir::visit::{
     MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
 };
 use rustc_middle::mir::{
-    AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind,
-    Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
-    StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
+    BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind, Location,
+    Operand, Place, Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnOp,
+    RETURN_PLACE,
 };
 use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::subst::{InternalSubsts, Subst};
-use rustc_middle::ty::{
-    self, ConstInt, ConstKind, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeFoldable,
-};
-use rustc_session::lint;
+use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable};
 use rustc_span::{def_id::DefId, Span};
 use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::traits;
 
 use crate::MirPass;
-use rustc_const_eval::const_eval::ConstEvalErr;
 use rustc_const_eval::interpret::{
     self, compile_time_machine, AllocId, ConstAllocation, ConstValue, CtfeValidationMode, Frame,
     ImmTy, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, MemoryKind, OpTy,
@@ -318,9 +313,8 @@ struct ConstPropagator<'mir, 'tcx> {
     ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
     tcx: TyCtxt<'tcx>,
     param_env: ParamEnv<'tcx>,
-    // FIXME(eddyb) avoid cloning these two fields more than once,
-    // by accessing them through `ecx` instead.
-    source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,
+    // FIXME(eddyb) avoid cloning this field more than once,
+    // by accessing it through `ecx` instead.
     local_decls: IndexVec<Local, LocalDecl<'tcx>>,
     // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
     // the last known `SourceInfo` here and just keep revisiting it.
@@ -412,9 +406,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             ecx,
             tcx,
             param_env,
-            // FIXME(eddyb) avoid cloning these two fields more than once,
-            // by accessing them through `ecx` instead.
-            source_scopes: body.source_scopes.clone(),
+            // FIXME(eddyb) avoid cloning this field more than once,
+            // by accessing it through `ecx` instead.
             //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
             local_decls: body.local_decls.clone(),
             source_info: None,
@@ -445,10 +438,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             LocalState { value: LocalValue::Unallocated, layout: Cell::new(None) };
     }
 
-    fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> {
-        source_info.scope.lint_root(&self.source_scopes)
-    }
-
     fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
     where
         F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
@@ -471,45 +460,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     }
 
     /// Returns the value, if any, of evaluating `c`.
-    fn eval_constant(&mut self, c: &Constant<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
+    fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<OpTy<'tcx>> {
         // FIXME we need to revisit this for #67176
         if c.needs_subst() {
             return None;
         }
 
-        match self.ecx.mir_const_to_op(&c.literal, None) {
-            Ok(op) => Some(op),
-            Err(error) => {
-                let tcx = self.ecx.tcx.at(c.span);
-                let err = ConstEvalErr::new(&self.ecx, error, Some(c.span));
-                if let Some(lint_root) = self.lint_root(source_info) {
-                    let lint_only = match c.literal {
-                        ConstantKind::Ty(ct) => match ct.val() {
-                            // Promoteds must lint and not error as the user didn't ask for them
-                            ConstKind::Unevaluated(ty::Unevaluated {
-                                def: _,
-                                substs: _,
-                                promoted: Some(_),
-                            }) => true,
-                            // Out of backwards compatibility we cannot report hard errors in unused
-                            // generic functions using associated constants of the generic parameters.
-                            _ => c.literal.needs_subst(),
-                        },
-                        ConstantKind::Val(_, ty) => ty.needs_subst(),
-                    };
-                    if lint_only {
-                        // Out of backwards compatibility we cannot report hard errors in unused
-                        // generic functions using associated constants of the generic parameters.
-                        err.report_as_lint(tcx, "erroneous constant used", lint_root, Some(c.span));
-                    } else {
-                        err.report_as_error(tcx, "erroneous constant used");
-                    }
-                } else {
-                    err.report_as_error(tcx, "erroneous constant used");
-                }
-                None
-            }
-        }
+        self.ecx.mir_const_to_op(&c.literal, None).ok()
     }
 
     /// Returns the value, if any, of evaluating `place`.
@@ -520,49 +477,22 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
 
     /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
     /// or `eval_place`, depending on the variant of `Operand` used.
-    fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
+    fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option<OpTy<'tcx>> {
         match *op {
-            Operand::Constant(ref c) => self.eval_constant(c, source_info),
+            Operand::Constant(ref c) => self.eval_constant(c),
             Operand::Move(place) | Operand::Copy(place) => self.eval_place(place),
         }
     }
 
-    fn report_assert_as_lint(
-        &self,
-        lint: &'static lint::Lint,
-        source_info: SourceInfo,
-        message: &'static str,
-        panic: AssertKind<impl std::fmt::Debug>,
-    ) {
-        if let Some(lint_root) = self.lint_root(source_info) {
-            self.tcx.struct_span_lint_hir(lint, lint_root, source_info.span, |lint| {
-                let mut err = lint.build(message);
-                err.span_label(source_info.span, format!("{:?}", panic));
-                err.emit();
-            });
-        }
-    }
-
-    fn check_unary_op(
-        &mut self,
-        op: UnOp,
-        arg: &Operand<'tcx>,
-        source_info: SourceInfo,
-    ) -> Option<()> {
-        if let (val, true) = self.use_ecx(|this| {
+    fn check_unary_op(&mut self, op: UnOp, arg: &Operand<'tcx>) -> Option<()> {
+        if self.use_ecx(|this| {
             let val = this.ecx.read_immediate(&this.ecx.eval_operand(arg, None)?)?;
             let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, &val)?;
-            Ok((val, overflow))
+            Ok(overflow)
         })? {
             // `AssertKind` only has an `OverflowNeg` variant, so make sure that is
             // appropriate to use.
             assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow");
-            self.report_assert_as_lint(
-                lint::builtin::ARITHMETIC_OVERFLOW,
-                source_info,
-                "this arithmetic operation will overflow",
-                AssertKind::OverflowNeg(val.to_const_int()),
-            );
             return None;
         }
 
@@ -574,7 +504,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         op: BinOp,
         left: &Operand<'tcx>,
         right: &Operand<'tcx>,
-        source_info: SourceInfo,
     ) -> Option<()> {
         let r = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(right, None)?));
         let l = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?));
@@ -589,25 +518,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             let r_bits = r.to_scalar().ok();
             let r_bits = r_bits.and_then(|r| r.to_bits(right_size).ok());
             if r_bits.map_or(false, |b| b >= left_size.bits() as u128) {
-                debug!("check_binary_op: reporting assert for {:?}", source_info);
-                self.report_assert_as_lint(
-                    lint::builtin::ARITHMETIC_OVERFLOW,
-                    source_info,
-                    "this arithmetic operation will overflow",
-                    AssertKind::Overflow(
-                        op,
-                        match l {
-                            Some(l) => l.to_const_int(),
-                            // Invent a dummy value, the diagnostic ignores it anyway
-                            None => ConstInt::new(
-                                ScalarInt::try_from_uint(1_u8, left_size).unwrap(),
-                                left_ty.is_signed(),
-                                left_ty.is_ptr_sized_integral(),
-                            ),
-                        },
-                        r.to_const_int(),
-                    ),
-                );
                 return None;
             }
         }
@@ -618,12 +528,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                 let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?;
                 Ok(overflow)
             })? {
-                self.report_assert_as_lint(
-                    lint::builtin::ARITHMETIC_OVERFLOW,
-                    source_info,
-                    "this arithmetic operation will overflow",
-                    AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()),
-                );
                 return None;
             }
         }
@@ -656,12 +560,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         }
     }
 
-    fn const_prop(
-        &mut self,
-        rvalue: &Rvalue<'tcx>,
-        source_info: SourceInfo,
-        place: Place<'tcx>,
-    ) -> Option<()> {
+    fn const_prop(&mut self, rvalue: &Rvalue<'tcx>, place: Place<'tcx>) -> Option<()> {
         // Perform any special handling for specific Rvalue types.
         // Generally, checks here fall into one of two categories:
         //   1. Additional checking to provide useful lints to the user
@@ -676,11 +575,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             // lint.
             Rvalue::UnaryOp(op, arg) => {
                 trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg);
-                self.check_unary_op(*op, arg, source_info)?;
+                self.check_unary_op(*op, arg)?;
             }
             Rvalue::BinaryOp(op, box (left, right)) => {
                 trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
-                self.check_binary_op(*op, left, right, source_info)?;
+                self.check_binary_op(*op, left, right)?;
             }
             Rvalue::CheckedBinaryOp(op, box (left, right)) => {
                 trace!(
@@ -689,7 +588,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                     left,
                     right
                 );
-                self.check_binary_op(*op, left, right, source_info)?;
+                self.check_binary_op(*op, left, right)?;
             }
 
             // Do not try creating references (#67862)
@@ -1071,7 +970,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
     fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
         trace!("visit_constant: {:?}", constant);
         self.super_constant(constant, location);
-        self.eval_constant(constant, self.source_info.unwrap());
+        self.eval_constant(constant);
     }
 
     fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
@@ -1080,7 +979,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
         self.source_info = Some(source_info);
         if let StatementKind::Assign(box (place, ref mut rval)) = statement.kind {
             let can_const_prop = self.ecx.machine.can_const_prop[place.local];
-            if let Some(()) = self.const_prop(rval, source_info, place) {
+            if let Some(()) = self.const_prop(rval, place) {
                 // This will return None if the above `const_prop` invocation only "wrote" a
                 // type whose creation requires no write. E.g. a generator whose initial state
                 // consists solely of uninitialized memory (so it doesn't capture any locals).
@@ -1164,57 +1063,12 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
         self.source_info = Some(source_info);
         self.super_terminator(terminator, location);
         match &mut terminator.kind {
-            TerminatorKind::Assert { expected, ref msg, ref mut cond, .. } => {
-                if let Some(ref value) = self.eval_operand(&cond, source_info) {
+            TerminatorKind::Assert { expected, ref mut cond, .. } => {
+                if let Some(ref value) = self.eval_operand(&cond) {
                     trace!("assertion on {:?} should be {:?}", value, expected);
                     let expected = ScalarMaybeUninit::from(Scalar::from_bool(*expected));
                     let value_const = self.ecx.read_scalar(&value).unwrap();
                     if expected != value_const {
-                        enum DbgVal<T> {
-                            Val(T),
-                            Underscore,
-                        }
-                        impl<T: std::fmt::Debug> std::fmt::Debug for DbgVal<T> {
-                            fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-                                match self {
-                                    Self::Val(val) => val.fmt(fmt),
-                                    Self::Underscore => fmt.write_str("_"),
-                                }
-                            }
-                        }
-                        let mut eval_to_int = |op| {
-                            // This can be `None` if the lhs wasn't const propagated and we just
-                            // triggered the assert on the value of the rhs.
-                            self.eval_operand(op, source_info).map_or(DbgVal::Underscore, |op| {
-                                DbgVal::Val(self.ecx.read_immediate(&op).unwrap().to_const_int())
-                            })
-                        };
-                        let msg = match msg {
-                            AssertKind::DivisionByZero(op) => {
-                                Some(AssertKind::DivisionByZero(eval_to_int(op)))
-                            }
-                            AssertKind::RemainderByZero(op) => {
-                                Some(AssertKind::RemainderByZero(eval_to_int(op)))
-                            }
-                            AssertKind::Overflow(bin_op @ (BinOp::Div | BinOp::Rem), op1, op2) => {
-                                // Division overflow is *UB* in the MIR, and different than the
-                                // other overflow checks.
-                                Some(AssertKind::Overflow(
-                                    *bin_op,
-                                    eval_to_int(op1),
-                                    eval_to_int(op2),
-                                ))
-                            }
-                            AssertKind::BoundsCheck { ref len, ref index } => {
-                                let len = eval_to_int(len);
-                                let index = eval_to_int(index);
-                                Some(AssertKind::BoundsCheck { len, index })
-                            }
-                            // Remaining overflow errors are already covered by checks on the binary operators.
-                            AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => None,
-                            // Need proper const propagator for these.
-                            _ => None,
-                        };
                         // Poison all places this operand references so that further code
                         // doesn't use the invalid value
                         match cond {
@@ -1223,14 +1077,6 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
                             }
                             Operand::Constant(_) => {}
                         }
-                        if let Some(msg) = msg {
-                            self.report_assert_as_lint(
-                                lint::builtin::UNCONDITIONAL_PANIC,
-                                source_info,
-                                "this operation will panic at runtime",
-                                msg,
-                            );
-                        }
                     } else {
                         if self.should_const_prop(value) {
                             if let ScalarMaybeUninit::Scalar(scalar) = value_const {
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
new file mode 100644
index 00000000000..5be745bc1de
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -0,0 +1,1035 @@
+//! Propagates constants for early reporting of statically known
+//! assertion failures
+
+use std::cell::Cell;
+
+use rustc_ast::Mutability;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::def::DefKind;
+use rustc_hir::HirId;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
+use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
+use rustc_middle::mir::{
+    AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind,
+    Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
+    StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
+};
+use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
+use rustc_middle::ty::subst::{InternalSubsts, Subst};
+use rustc_middle::ty::{
+    self, ConstInt, ConstKind, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeFoldable,
+};
+use rustc_session::lint;
+use rustc_span::{def_id::DefId, Span};
+use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
+use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::traits;
+
+use crate::MirLint;
+use rustc_const_eval::const_eval::ConstEvalErr;
+use rustc_const_eval::interpret::{
+    self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
+    LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Scalar,
+    ScalarMaybeUninit, StackPopCleanup, StackPopUnwind,
+};
+
+/// The maximum number of bytes that we'll allocate space for a local or the return value.
+/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
+/// Severely regress performance.
+const MAX_ALLOC_LIMIT: u64 = 1024;
+
+/// Macro for machine-specific `InterpError` without allocation.
+/// (These will never be shown to the user, but they help diagnose ICEs.)
+macro_rules! throw_machine_stop_str {
+    ($($tt:tt)*) => {{
+        // We make a new local type for it. The type itself does not carry any information,
+        // but its vtable (for the `MachineStopType` trait) does.
+        struct Zst;
+        // Printing this type shows the desired string.
+        impl std::fmt::Display for Zst {
+            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+                write!(f, $($tt)*)
+            }
+        }
+        impl rustc_middle::mir::interpret::MachineStopType for Zst {}
+        throw_machine_stop!(Zst)
+    }};
+}
+
+pub struct ConstProp;
+
+impl<'tcx> MirLint<'tcx> for ConstProp {
+    fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
+        // will be evaluated by miri and produce its errors there
+        if body.source.promoted.is_some() {
+            return;
+        }
+
+        let def_id = body.source.def_id().expect_local();
+        let is_fn_like = tcx.hir().get_by_def_id(def_id).fn_kind().is_some();
+        let is_assoc_const = tcx.def_kind(def_id) == DefKind::AssocConst;
+
+        // Only run const prop on functions, methods, closures and associated constants
+        if !is_fn_like && !is_assoc_const {
+            // skip anon_const/statics/consts because they'll be evaluated by miri anyway
+            trace!("ConstProp skipped for {:?}", def_id);
+            return;
+        }
+
+        let is_generator = tcx.type_of(def_id.to_def_id()).is_generator();
+        // FIXME(welseywiser) const prop doesn't work on generators because of query cycles
+        // computing their layout.
+        if is_generator {
+            trace!("ConstProp skipped for generator {:?}", def_id);
+            return;
+        }
+
+        // Check if it's even possible to satisfy the 'where' clauses
+        // for this item.
+        // This branch will never be taken for any normal function.
+        // However, it's possible to `#!feature(trivial_bounds)]` to write
+        // a function with impossible to satisfy clauses, e.g.:
+        // `fn foo() where String: Copy {}`
+        //
+        // We don't usually need to worry about this kind of case,
+        // since we would get a compilation error if the user tried
+        // to call it. However, since we can do const propagation
+        // even without any calls to the function, we need to make
+        // sure that it even makes sense to try to evaluate the body.
+        // If there are unsatisfiable where clauses, then all bets are
+        // off, and we just give up.
+        //
+        // We manually filter the predicates, skipping anything that's not
+        // "global". We are in a potentially generic context
+        // (e.g. we are evaluating a function without substituting generic
+        // parameters, so this filtering serves two purposes:
+        //
+        // 1. We skip evaluating any predicates that we would
+        // never be able prove are unsatisfiable (e.g. `<T as Foo>`
+        // 2. We avoid trying to normalize predicates involving generic
+        // parameters (e.g. `<T as Foo>::MyItem`). This can confuse
+        // the normalization code (leading to cycle errors), since
+        // it's usually never invoked in this way.
+        let predicates = tcx
+            .predicates_of(def_id.to_def_id())
+            .predicates
+            .iter()
+            .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
+        if traits::impossible_predicates(
+            tcx,
+            traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(),
+        ) {
+            trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
+            return;
+        }
+
+        trace!("ConstProp starting for {:?}", def_id);
+
+        let dummy_body = &Body::new(
+            body.source,
+            body.basic_blocks().clone(),
+            body.source_scopes.clone(),
+            body.local_decls.clone(),
+            Default::default(),
+            body.arg_count,
+            Default::default(),
+            body.span,
+            body.generator_kind(),
+            body.tainted_by_errors,
+        );
+
+        // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
+        // constants, instead of just checking for const-folding succeeding.
+        // That would require a uniform one-def no-mutation analysis
+        // and RPO (or recursing when needing the value of a local).
+        let mut optimization_finder = ConstPropagator::new(body, dummy_body, tcx);
+        optimization_finder.visit_body(body);
+
+        trace!("ConstProp done for {:?}", def_id);
+    }
+}
+
+struct ConstPropMachine<'mir, 'tcx> {
+    /// The virtual call stack.
+    stack: Vec<Frame<'mir, 'tcx>>,
+    /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end.
+    written_only_inside_own_block_locals: FxHashSet<Local>,
+    /// Locals that need to be cleared after every block terminates.
+    only_propagate_inside_block_locals: BitSet<Local>,
+    can_const_prop: IndexVec<Local, ConstPropMode>,
+}
+
+impl ConstPropMachine<'_, '_> {
+    fn new(
+        only_propagate_inside_block_locals: BitSet<Local>,
+        can_const_prop: IndexVec<Local, ConstPropMode>,
+    ) -> Self {
+        Self {
+            stack: Vec::new(),
+            written_only_inside_own_block_locals: Default::default(),
+            only_propagate_inside_block_locals,
+            can_const_prop,
+        }
+    }
+}
+
+impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> {
+    compile_time_machine!(<'mir, 'tcx>);
+    const PANIC_ON_ALLOC_FAIL: bool = true; // all allocations are small (see `MAX_ALLOC_LIMIT`)
+
+    type MemoryKind = !;
+
+    type MemoryExtra = ();
+
+    fn load_mir(
+        _ecx: &InterpCx<'mir, 'tcx, Self>,
+        _instance: ty::InstanceDef<'tcx>,
+    ) -> InterpResult<'tcx, &'tcx Body<'tcx>> {
+        throw_machine_stop_str!("calling functions isn't supported in ConstProp")
+    }
+
+    fn find_mir_or_eval_fn(
+        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        _instance: ty::Instance<'tcx>,
+        _abi: Abi,
+        _args: &[OpTy<'tcx>],
+        _ret: Option<(&PlaceTy<'tcx>, BasicBlock)>,
+        _unwind: StackPopUnwind,
+    ) -> InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> {
+        Ok(None)
+    }
+
+    fn call_intrinsic(
+        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        _instance: ty::Instance<'tcx>,
+        _args: &[OpTy<'tcx>],
+        _ret: Option<(&PlaceTy<'tcx>, BasicBlock)>,
+        _unwind: StackPopUnwind,
+    ) -> InterpResult<'tcx> {
+        throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp")
+    }
+
+    fn assert_panic(
+        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        _msg: &rustc_middle::mir::AssertMessage<'tcx>,
+        _unwind: Option<rustc_middle::mir::BasicBlock>,
+    ) -> InterpResult<'tcx> {
+        bug!("panics terminators are not evaluated in ConstProp")
+    }
+
+    fn binary_ptr_op(
+        _ecx: &InterpCx<'mir, 'tcx, Self>,
+        _bin_op: BinOp,
+        _left: &ImmTy<'tcx>,
+        _right: &ImmTy<'tcx>,
+    ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
+        // We can't do this because aliasing of memory can differ between const eval and llvm
+        throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp")
+    }
+
+    fn access_local(
+        _ecx: &InterpCx<'mir, 'tcx, Self>,
+        frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
+        local: Local,
+    ) -> InterpResult<'tcx, InterpOperand<Self::PointerTag>> {
+        let l = &frame.locals[local];
+
+        if l.value == LocalValue::Unallocated {
+            throw_machine_stop_str!("tried to access an uninitialized local")
+        }
+
+        l.access()
+    }
+
+    fn access_local_mut<'a>(
+        ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
+        frame: usize,
+        local: Local,
+    ) -> InterpResult<'tcx, Result<&'a mut LocalValue<Self::PointerTag>, MemPlace<Self::PointerTag>>>
+    {
+        if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation {
+            throw_machine_stop_str!("tried to write to a local that is marked as not propagatable")
+        }
+        if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) {
+            trace!(
+                "mutating local {:?} which is restricted to its block. \
+                Will remove it from const-prop after block is finished.",
+                local
+            );
+            ecx.machine.written_only_inside_own_block_locals.insert(local);
+        }
+        ecx.machine.stack[frame].locals[local].access_mut()
+    }
+
+    fn before_access_global(
+        _memory_extra: &(),
+        _alloc_id: AllocId,
+        alloc: ConstAllocation<'tcx, Self::PointerTag, Self::AllocExtra>,
+        _static_def_id: Option<DefId>,
+        is_write: bool,
+    ) -> InterpResult<'tcx> {
+        if is_write {
+            throw_machine_stop_str!("can't write to global");
+        }
+        // If the static allocation is mutable, then we can't const prop it as its content
+        // might be different at runtime.
+        if alloc.inner().mutability == Mutability::Mut {
+            throw_machine_stop_str!("can't access mutable globals in ConstProp");
+        }
+
+        Ok(())
+    }
+
+    #[inline(always)]
+    fn init_frame_extra(
+        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        frame: Frame<'mir, 'tcx>,
+    ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> {
+        Ok(frame)
+    }
+
+    #[inline(always)]
+    fn stack<'a>(
+        ecx: &'a InterpCx<'mir, 'tcx, Self>,
+    ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
+        &ecx.machine.stack
+    }
+
+    #[inline(always)]
+    fn stack_mut<'a>(
+        ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
+    ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
+        &mut ecx.machine.stack
+    }
+}
+
+/// Finds optimization opportunities on the MIR.
+struct ConstPropagator<'mir, 'tcx> {
+    ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
+    tcx: TyCtxt<'tcx>,
+    param_env: ParamEnv<'tcx>,
+    // FIXME(eddyb) avoid cloning these two fields more than once,
+    // by accessing them through `ecx` instead.
+    source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,
+    local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+    // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
+    // the last known `SourceInfo` here and just keep revisiting it.
+    source_info: Option<SourceInfo>,
+}
+
+impl<'tcx> LayoutOfHelpers<'tcx> for ConstPropagator<'_, 'tcx> {
+    type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
+
+    #[inline]
+    fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
+        err
+    }
+}
+
+impl HasDataLayout for ConstPropagator<'_, '_> {
+    #[inline]
+    fn data_layout(&self) -> &TargetDataLayout {
+        &self.tcx.data_layout
+    }
+}
+
+impl<'tcx> ty::layout::HasTyCtxt<'tcx> for ConstPropagator<'_, 'tcx> {
+    #[inline]
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+}
+
+impl<'tcx> ty::layout::HasParamEnv<'tcx> for ConstPropagator<'_, 'tcx> {
+    #[inline]
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.param_env
+    }
+}
+
+impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
+    fn new(
+        body: &Body<'tcx>,
+        dummy_body: &'mir Body<'tcx>,
+        tcx: TyCtxt<'tcx>,
+    ) -> ConstPropagator<'mir, 'tcx> {
+        let def_id = body.source.def_id();
+        let substs = &InternalSubsts::identity_for_item(tcx, def_id);
+        let param_env = tcx.param_env_reveal_all_normalized(def_id);
+
+        let span = tcx.def_span(def_id);
+        // FIXME: `CanConstProp::check` computes the layout of all locals, return those layouts
+        // so we can write them to `ecx.frame_mut().locals.layout, reducing the duplication in
+        // `layout_of` query invocations.
+        let can_const_prop = CanConstProp::check(tcx, param_env, body);
+        let mut only_propagate_inside_block_locals = BitSet::new_empty(can_const_prop.len());
+        for (l, mode) in can_const_prop.iter_enumerated() {
+            if *mode == ConstPropMode::OnlyInsideOwnBlock {
+                only_propagate_inside_block_locals.insert(l);
+            }
+        }
+        let mut ecx = InterpCx::new(
+            tcx,
+            span,
+            param_env,
+            ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop),
+            (),
+        );
+
+        let ret = ecx
+            .layout_of(body.return_ty().subst(tcx, substs))
+            .ok()
+            // Don't bother allocating memory for ZST types which have no values
+            // or for large values.
+            .filter(|ret_layout| {
+                !ret_layout.is_zst() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)
+            })
+            .map(|ret_layout| {
+                ecx.allocate(ret_layout, MemoryKind::Stack)
+                    .expect("couldn't perform small allocation")
+                    .into()
+            });
+
+        ecx.push_stack_frame(
+            Instance::new(def_id, substs),
+            dummy_body,
+            ret.as_ref(),
+            StackPopCleanup::Root { cleanup: false },
+        )
+        .expect("failed to push initial stack frame");
+
+        ConstPropagator {
+            ecx,
+            tcx,
+            param_env,
+            // FIXME(eddyb) avoid cloning these two fields more than once,
+            // by accessing them through `ecx` instead.
+            source_scopes: body.source_scopes.clone(),
+            //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
+            local_decls: body.local_decls.clone(),
+            source_info: None,
+        }
+    }
+
+    fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
+        let op = match self.ecx.eval_place_to_op(place, None) {
+            Ok(op) => op,
+            Err(e) => {
+                trace!("get_const failed: {}", e);
+                return None;
+            }
+        };
+
+        // Try to read the local as an immediate so that if it is representable as a scalar, we can
+        // handle it as such, but otherwise, just return the value as is.
+        Some(match self.ecx.try_read_immediate(&op) {
+            Ok(Ok(imm)) => imm.into(),
+            _ => op,
+        })
+    }
+
+    /// Remove `local` from the pool of `Locals`. Allows writing to them,
+    /// but not reading from them anymore.
+    fn remove_const(ecx: &mut InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, local: Local) {
+        ecx.frame_mut().locals[local] =
+            LocalState { value: LocalValue::Unallocated, layout: Cell::new(None) };
+    }
+
+    fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> {
+        source_info.scope.lint_root(&self.source_scopes)
+    }
+
+    fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
+    where
+        F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
+    {
+        match f(self) {
+            Ok(val) => Some(val),
+            Err(error) => {
+                trace!("InterpCx operation failed: {:?}", error);
+                // Some errors shouldn't come up because creating them causes
+                // an allocation, which we should avoid. When that happens,
+                // dedicated error variants should be introduced instead.
+                assert!(
+                    !error.kind().formatted_string(),
+                    "const-prop encountered formatting error: {}",
+                    error
+                );
+                None
+            }
+        }
+    }
+
+    /// Returns the value, if any, of evaluating `c`.
+    fn eval_constant(&mut self, c: &Constant<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
+        // FIXME we need to revisit this for #67176
+        if c.needs_subst() {
+            return None;
+        }
+
+        match self.ecx.mir_const_to_op(&c.literal, None) {
+            Ok(op) => Some(op),
+            Err(error) => {
+                let tcx = self.ecx.tcx.at(c.span);
+                let err = ConstEvalErr::new(&self.ecx, error, Some(c.span));
+                if let Some(lint_root) = self.lint_root(source_info) {
+                    let lint_only = match c.literal {
+                        ConstantKind::Ty(ct) => match ct.val() {
+                            // Promoteds must lint and not error as the user didn't ask for them
+                            ConstKind::Unevaluated(ty::Unevaluated {
+                                def: _,
+                                substs: _,
+                                promoted: Some(_),
+                            }) => true,
+                            // Out of backwards compatibility we cannot report hard errors in unused
+                            // generic functions using associated constants of the generic parameters.
+                            _ => c.literal.needs_subst(),
+                        },
+                        ConstantKind::Val(_, ty) => ty.needs_subst(),
+                    };
+                    if lint_only {
+                        // Out of backwards compatibility we cannot report hard errors in unused
+                        // generic functions using associated constants of the generic parameters.
+                        err.report_as_lint(tcx, "erroneous constant used", lint_root, Some(c.span));
+                    } else {
+                        err.report_as_error(tcx, "erroneous constant used");
+                    }
+                } else {
+                    err.report_as_error(tcx, "erroneous constant used");
+                }
+                None
+            }
+        }
+    }
+
+    /// Returns the value, if any, of evaluating `place`.
+    fn eval_place(&mut self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
+        trace!("eval_place(place={:?})", place);
+        self.use_ecx(|this| this.ecx.eval_place_to_op(place, None))
+    }
+
+    /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
+    /// or `eval_place`, depending on the variant of `Operand` used.
+    fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
+        match *op {
+            Operand::Constant(ref c) => self.eval_constant(c, source_info),
+            Operand::Move(place) | Operand::Copy(place) => self.eval_place(place),
+        }
+    }
+
+    fn report_assert_as_lint(
+        &self,
+        lint: &'static lint::Lint,
+        source_info: SourceInfo,
+        message: &'static str,
+        panic: AssertKind<impl std::fmt::Debug>,
+    ) {
+        if let Some(lint_root) = self.lint_root(source_info) {
+            self.tcx.struct_span_lint_hir(lint, lint_root, source_info.span, |lint| {
+                let mut err = lint.build(message);
+                err.span_label(source_info.span, format!("{:?}", panic));
+                err.emit();
+            });
+        }
+    }
+
+    fn check_unary_op(
+        &mut self,
+        op: UnOp,
+        arg: &Operand<'tcx>,
+        source_info: SourceInfo,
+    ) -> Option<()> {
+        if let (val, true) = self.use_ecx(|this| {
+            let val = this.ecx.read_immediate(&this.ecx.eval_operand(arg, None)?)?;
+            let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, &val)?;
+            Ok((val, overflow))
+        })? {
+            // `AssertKind` only has an `OverflowNeg` variant, so make sure that is
+            // appropriate to use.
+            assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow");
+            self.report_assert_as_lint(
+                lint::builtin::ARITHMETIC_OVERFLOW,
+                source_info,
+                "this arithmetic operation will overflow",
+                AssertKind::OverflowNeg(val.to_const_int()),
+            );
+            return None;
+        }
+
+        Some(())
+    }
+
+    fn check_binary_op(
+        &mut self,
+        op: BinOp,
+        left: &Operand<'tcx>,
+        right: &Operand<'tcx>,
+        source_info: SourceInfo,
+    ) -> Option<()> {
+        let r = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(right, None)?));
+        let l = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?));
+        // Check for exceeding shifts *even if* we cannot evaluate the LHS.
+        if op == BinOp::Shr || op == BinOp::Shl {
+            let r = r?;
+            // We need the type of the LHS. We cannot use `place_layout` as that is the type
+            // of the result, which for checked binops is not the same!
+            let left_ty = left.ty(&self.local_decls, self.tcx);
+            let left_size = self.ecx.layout_of(left_ty).ok()?.size;
+            let right_size = r.layout.size;
+            let r_bits = r.to_scalar().ok();
+            let r_bits = r_bits.and_then(|r| r.to_bits(right_size).ok());
+            if r_bits.map_or(false, |b| b >= left_size.bits() as u128) {
+                debug!("check_binary_op: reporting assert for {:?}", source_info);
+                self.report_assert_as_lint(
+                    lint::builtin::ARITHMETIC_OVERFLOW,
+                    source_info,
+                    "this arithmetic operation will overflow",
+                    AssertKind::Overflow(
+                        op,
+                        match l {
+                            Some(l) => l.to_const_int(),
+                            // Invent a dummy value, the diagnostic ignores it anyway
+                            None => ConstInt::new(
+                                ScalarInt::try_from_uint(1_u8, left_size).unwrap(),
+                                left_ty.is_signed(),
+                                left_ty.is_ptr_sized_integral(),
+                            ),
+                        },
+                        r.to_const_int(),
+                    ),
+                );
+                return None;
+            }
+        }
+
+        if let (Some(l), Some(r)) = (&l, &r) {
+            // The remaining operators are handled through `overflowing_binary_op`.
+            if self.use_ecx(|this| {
+                let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?;
+                Ok(overflow)
+            })? {
+                self.report_assert_as_lint(
+                    lint::builtin::ARITHMETIC_OVERFLOW,
+                    source_info,
+                    "this arithmetic operation will overflow",
+                    AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()),
+                );
+                return None;
+            }
+        }
+        Some(())
+    }
+
+    fn const_prop(
+        &mut self,
+        rvalue: &Rvalue<'tcx>,
+        source_info: SourceInfo,
+        place: Place<'tcx>,
+    ) -> Option<()> {
+        // Perform any special handling for specific Rvalue types.
+        // Generally, checks here fall into one of two categories:
+        //   1. Additional checking to provide useful lints to the user
+        //        - In this case, we will do some validation and then fall through to the
+        //          end of the function which evals the assignment.
+        //   2. Working around bugs in other parts of the compiler
+        //        - In this case, we'll return `None` from this function to stop evaluation.
+        match rvalue {
+            // Additional checking: give lints to the user if an overflow would occur.
+            // We do this here and not in the `Assert` terminator as that terminator is
+            // only sometimes emitted (overflow checks can be disabled), but we want to always
+            // lint.
+            Rvalue::UnaryOp(op, arg) => {
+                trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg);
+                self.check_unary_op(*op, arg, source_info)?;
+            }
+            Rvalue::BinaryOp(op, box (left, right)) => {
+                trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
+                self.check_binary_op(*op, left, right, source_info)?;
+            }
+            Rvalue::CheckedBinaryOp(op, box (left, right)) => {
+                trace!(
+                    "checking CheckedBinaryOp(op = {:?}, left = {:?}, right = {:?})",
+                    op,
+                    left,
+                    right
+                );
+                self.check_binary_op(*op, left, right, source_info)?;
+            }
+
+            // Do not try creating references (#67862)
+            Rvalue::AddressOf(_, place) | Rvalue::Ref(_, _, place) => {
+                trace!("skipping AddressOf | Ref for {:?}", place);
+
+                // This may be creating mutable references or immutable references to cells.
+                // If that happens, the pointed to value could be mutated via that reference.
+                // Since we aren't tracking references, the const propagator loses track of what
+                // value the local has right now.
+                // Thus, all locals that have their reference taken
+                // must not take part in propagation.
+                Self::remove_const(&mut self.ecx, place.local);
+
+                return None;
+            }
+            Rvalue::ThreadLocalRef(def_id) => {
+                trace!("skipping ThreadLocalRef({:?})", def_id);
+
+                return None;
+            }
+
+            // There's no other checking to do at this time.
+            Rvalue::Aggregate(..)
+            | Rvalue::Use(..)
+            | Rvalue::Repeat(..)
+            | Rvalue::Len(..)
+            | Rvalue::Cast(..)
+            | Rvalue::ShallowInitBox(..)
+            | Rvalue::Discriminant(..)
+            | Rvalue::NullaryOp(..) => {}
+        }
+
+        // FIXME we need to revisit this for #67176
+        if rvalue.needs_subst() {
+            return None;
+        }
+
+        self.use_ecx(|this| this.ecx.eval_rvalue_into_place(rvalue, place))
+    }
+}
+
+/// The mode that `ConstProp` is allowed to run in for a given `Local`.
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum ConstPropMode {
+    /// The `Local` can be propagated into and reads of this `Local` can also be propagated.
+    FullConstProp,
+    /// The `Local` can only be propagated into and from its own block.
+    OnlyInsideOwnBlock,
+    /// The `Local` can be propagated into but reads cannot be propagated.
+    OnlyPropagateInto,
+    /// The `Local` cannot be part of propagation at all. Any statement
+    /// referencing it either for reading or writing will not get propagated.
+    NoPropagation,
+}
+
+struct CanConstProp {
+    can_const_prop: IndexVec<Local, ConstPropMode>,
+    // False at the beginning. Once set, no more assignments are allowed to that local.
+    found_assignment: BitSet<Local>,
+    // Cache of locals' information
+    local_kinds: IndexVec<Local, LocalKind>,
+}
+
+impl CanConstProp {
+    /// Returns true if `local` can be propagated
+    fn check<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        param_env: ParamEnv<'tcx>,
+        body: &Body<'tcx>,
+    ) -> IndexVec<Local, ConstPropMode> {
+        let mut cpv = CanConstProp {
+            can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
+            found_assignment: BitSet::new_empty(body.local_decls.len()),
+            local_kinds: IndexVec::from_fn_n(
+                |local| body.local_kind(local),
+                body.local_decls.len(),
+            ),
+        };
+        for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
+            let ty = body.local_decls[local].ty;
+            match tcx.layout_of(param_env.and(ty)) {
+                Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {}
+                // Either the layout fails to compute, then we can't use this local anyway
+                // or the local is too large, then we don't want to.
+                _ => {
+                    *val = ConstPropMode::NoPropagation;
+                    continue;
+                }
+            }
+            // Cannot use args at all
+            // Cannot use locals because if x < y { y - x } else { x - y } would
+            //        lint for x != y
+            // FIXME(oli-obk): lint variables until they are used in a condition
+            // FIXME(oli-obk): lint if return value is constant
+            if cpv.local_kinds[local] == LocalKind::Arg {
+                *val = ConstPropMode::OnlyPropagateInto;
+                trace!(
+                    "local {:?} can't be const propagated because it's a function argument",
+                    local
+                );
+            } else if cpv.local_kinds[local] == LocalKind::Var {
+                *val = ConstPropMode::OnlyInsideOwnBlock;
+                trace!(
+                    "local {:?} will only be propagated inside its block, because it's a user variable",
+                    local
+                );
+            }
+        }
+        cpv.visit_body(&body);
+        cpv.can_const_prop
+    }
+}
+
+impl Visitor<'_> for CanConstProp {
+    fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
+        use rustc_middle::mir::visit::PlaceContext::*;
+        match context {
+            // Projections are fine, because `&mut foo.x` will be caught by
+            // `MutatingUseContext::Borrow` elsewhere.
+            MutatingUse(MutatingUseContext::Projection)
+            // These are just stores, where the storing is not propagatable, but there may be later
+            // mutations of the same local via `Store`
+            | MutatingUse(MutatingUseContext::Call)
+            | MutatingUse(MutatingUseContext::AsmOutput)
+            // Actual store that can possibly even propagate a value
+            | MutatingUse(MutatingUseContext::Store) => {
+                if !self.found_assignment.insert(local) {
+                    match &mut self.can_const_prop[local] {
+                        // If the local can only get propagated in its own block, then we don't have
+                        // to worry about multiple assignments, as we'll nuke the const state at the
+                        // end of the block anyway, and inside the block we overwrite previous
+                        // states as applicable.
+                        ConstPropMode::OnlyInsideOwnBlock => {}
+                        ConstPropMode::NoPropagation => {}
+                        ConstPropMode::OnlyPropagateInto => {}
+                        other @ ConstPropMode::FullConstProp => {
+                            trace!(
+                                "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}",
+                                local, other,
+                            );
+                            *other = ConstPropMode::OnlyInsideOwnBlock;
+                        }
+                    }
+                }
+            }
+            // Reading constants is allowed an arbitrary number of times
+            NonMutatingUse(NonMutatingUseContext::Copy)
+            | NonMutatingUse(NonMutatingUseContext::Move)
+            | NonMutatingUse(NonMutatingUseContext::Inspect)
+            | NonMutatingUse(NonMutatingUseContext::Projection)
+            | NonUse(_) => {}
+
+            // These could be propagated with a smarter analysis or just some careful thinking about
+            // whether they'd be fine right now.
+            MutatingUse(MutatingUseContext::Yield)
+            | MutatingUse(MutatingUseContext::Drop)
+            | MutatingUse(MutatingUseContext::Retag)
+            // These can't ever be propagated under any scheme, as we can't reason about indirect
+            // mutation.
+            | NonMutatingUse(NonMutatingUseContext::SharedBorrow)
+            | NonMutatingUse(NonMutatingUseContext::ShallowBorrow)
+            | NonMutatingUse(NonMutatingUseContext::UniqueBorrow)
+            | NonMutatingUse(NonMutatingUseContext::AddressOf)
+            | MutatingUse(MutatingUseContext::Borrow)
+            | MutatingUse(MutatingUseContext::AddressOf) => {
+                trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
+                self.can_const_prop[local] = ConstPropMode::NoPropagation;
+            }
+        }
+    }
+}
+
+impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
+    fn visit_body(&mut self, body: &Body<'tcx>) {
+        for (bb, data) in body.basic_blocks().iter_enumerated() {
+            self.visit_basic_block_data(bb, data);
+        }
+    }
+
+    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
+        self.super_operand(operand, location);
+    }
+
+    fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
+        trace!("visit_constant: {:?}", constant);
+        self.super_constant(constant, location);
+        self.eval_constant(constant, self.source_info.unwrap());
+    }
+
+    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
+        trace!("visit_statement: {:?}", statement);
+        let source_info = statement.source_info;
+        self.source_info = Some(source_info);
+        if let StatementKind::Assign(box (place, ref rval)) = statement.kind {
+            let can_const_prop = self.ecx.machine.can_const_prop[place.local];
+            if let Some(()) = self.const_prop(rval, source_info, place) {
+                match can_const_prop {
+                    ConstPropMode::OnlyInsideOwnBlock => {
+                        trace!(
+                            "found local restricted to its block. \
+                                Will remove it from const-prop after block is finished. Local: {:?}",
+                            place.local
+                        );
+                    }
+                    ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => {
+                        trace!("can't propagate into {:?}", place);
+                        if place.local != RETURN_PLACE {
+                            Self::remove_const(&mut self.ecx, place.local);
+                        }
+                    }
+                    ConstPropMode::FullConstProp => {}
+                }
+            } else {
+                // Const prop failed, so erase the destination, ensuring that whatever happens
+                // from here on, does not know about the previous value.
+                // This is important in case we have
+                // ```rust
+                // let mut x = 42;
+                // x = SOME_MUTABLE_STATIC;
+                // // x must now be uninit
+                // ```
+                // FIXME: we overzealously erase the entire local, because that's easier to
+                // implement.
+                trace!(
+                    "propagation into {:?} failed.
+                        Nuking the entire site from orbit, it's the only way to be sure",
+                    place,
+                );
+                Self::remove_const(&mut self.ecx, place.local);
+            }
+        } else {
+            match statement.kind {
+                StatementKind::SetDiscriminant { ref place, .. } => {
+                    match self.ecx.machine.can_const_prop[place.local] {
+                        ConstPropMode::FullConstProp | ConstPropMode::OnlyInsideOwnBlock => {
+                            if self.use_ecx(|this| this.ecx.statement(statement)).is_some() {
+                                trace!("propped discriminant into {:?}", place);
+                            } else {
+                                Self::remove_const(&mut self.ecx, place.local);
+                            }
+                        }
+                        ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => {
+                            Self::remove_const(&mut self.ecx, place.local);
+                        }
+                    }
+                }
+                StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
+                    let frame = self.ecx.frame_mut();
+                    frame.locals[local].value =
+                        if let StatementKind::StorageLive(_) = statement.kind {
+                            LocalValue::Unallocated
+                        } else {
+                            LocalValue::Dead
+                        };
+                }
+                _ => {}
+            }
+        }
+
+        self.super_statement(statement, location);
+    }
+
+    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
+        let source_info = terminator.source_info;
+        self.source_info = Some(source_info);
+        self.super_terminator(terminator, location);
+        match &terminator.kind {
+            TerminatorKind::Assert { expected, ref msg, ref cond, .. } => {
+                if let Some(ref value) = self.eval_operand(&cond, source_info) {
+                    trace!("assertion on {:?} should be {:?}", value, expected);
+                    let expected = ScalarMaybeUninit::from(Scalar::from_bool(*expected));
+                    let value_const = self.ecx.read_scalar(&value).unwrap();
+                    if expected != value_const {
+                        enum DbgVal<T> {
+                            Val(T),
+                            Underscore,
+                        }
+                        impl<T: std::fmt::Debug> std::fmt::Debug for DbgVal<T> {
+                            fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+                                match self {
+                                    Self::Val(val) => val.fmt(fmt),
+                                    Self::Underscore => fmt.write_str("_"),
+                                }
+                            }
+                        }
+                        let mut eval_to_int = |op| {
+                            // This can be `None` if the lhs wasn't const propagated and we just
+                            // triggered the assert on the value of the rhs.
+                            self.eval_operand(op, source_info).map_or(DbgVal::Underscore, |op| {
+                                DbgVal::Val(self.ecx.read_immediate(&op).unwrap().to_const_int())
+                            })
+                        };
+                        let msg = match msg {
+                            AssertKind::DivisionByZero(op) => {
+                                Some(AssertKind::DivisionByZero(eval_to_int(op)))
+                            }
+                            AssertKind::RemainderByZero(op) => {
+                                Some(AssertKind::RemainderByZero(eval_to_int(op)))
+                            }
+                            AssertKind::Overflow(bin_op @ (BinOp::Div | BinOp::Rem), op1, op2) => {
+                                // Division overflow is *UB* in the MIR, and different than the
+                                // other overflow checks.
+                                Some(AssertKind::Overflow(
+                                    *bin_op,
+                                    eval_to_int(op1),
+                                    eval_to_int(op2),
+                                ))
+                            }
+                            AssertKind::BoundsCheck { ref len, ref index } => {
+                                let len = eval_to_int(len);
+                                let index = eval_to_int(index);
+                                Some(AssertKind::BoundsCheck { len, index })
+                            }
+                            // Remaining overflow errors are already covered by checks on the binary operators.
+                            AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => None,
+                            // Need proper const propagator for these.
+                            _ => None,
+                        };
+                        // Poison all places this operand references so that further code
+                        // doesn't use the invalid value
+                        match cond {
+                            Operand::Move(ref place) | Operand::Copy(ref place) => {
+                                Self::remove_const(&mut self.ecx, place.local);
+                            }
+                            Operand::Constant(_) => {}
+                        }
+                        if let Some(msg) = msg {
+                            self.report_assert_as_lint(
+                                lint::builtin::UNCONDITIONAL_PANIC,
+                                source_info,
+                                "this operation will panic at runtime",
+                                msg,
+                            );
+                        }
+                    }
+                }
+            }
+            // None of these have Operands to const-propagate.
+            TerminatorKind::Goto { .. }
+            | TerminatorKind::Resume
+            | TerminatorKind::Abort
+            | TerminatorKind::Return
+            | TerminatorKind::Unreachable
+            | TerminatorKind::Drop { .. }
+            | TerminatorKind::DropAndReplace { .. }
+            | TerminatorKind::Yield { .. }
+            | TerminatorKind::GeneratorDrop
+            | TerminatorKind::FalseEdge { .. }
+            | TerminatorKind::FalseUnwind { .. }
+            | TerminatorKind::SwitchInt { .. }
+            | TerminatorKind::Call { .. }
+            | TerminatorKind::InlineAsm { .. } => {}
+        }
+
+        // We remove all Locals which are restricted in propagation to their containing blocks and
+        // which were modified in the current block.
+        // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`.
+        let mut locals = std::mem::take(&mut self.ecx.machine.written_only_inside_own_block_locals);
+        for &local in locals.iter() {
+            Self::remove_const(&mut self.ecx, local);
+        }
+        locals.clear();
+        // Put it back so we reuse the heap of the storage
+        self.ecx.machine.written_only_inside_own_block_locals = locals;
+        if cfg!(debug_assertions) {
+            // Ensure we are correctly erasing locals with the non-debug-assert logic.
+            for local in self.ecx.machine.only_propagate_inside_block_locals.iter() {
+                assert!(
+                    self.get_const(local.into()).is_none()
+                        || self
+                            .layout_of(self.local_decls[local].ty)
+                            .map_or(true, |layout| layout.is_zst())
+                )
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_mir_transform/src/deaggregator.rs b/compiler/rustc_mir_transform/src/deaggregator.rs
index 44753c5f631..616ba819982 100644
--- a/compiler/rustc_mir_transform/src/deaggregator.rs
+++ b/compiler/rustc_mir_transform/src/deaggregator.rs
@@ -6,6 +6,10 @@ use rustc_middle::ty::TyCtxt;
 pub struct Deaggregator;
 
 impl<'tcx> MirPass<'tcx> for Deaggregator {
+    fn phase_change(&self) -> Option<MirPhase> {
+        Some(MirPhase::Deaggregated)
+    }
+
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
         let local_decls = &*local_decls;
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index a4b1d86ff61..f78c7a084d8 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -20,7 +20,7 @@ pub struct ElaborateDrops;
 
 impl<'tcx> MirPass<'tcx> for ElaborateDrops {
     fn phase_change(&self) -> Option<MirPhase> {
-        Some(MirPhase::DropLowering)
+        Some(MirPhase::DropsLowered)
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index 8bc7f5e9ce2..ad96bf544cb 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -1235,7 +1235,7 @@ fn create_cases<'tcx>(
 
 impl<'tcx> MirPass<'tcx> for StateTransform {
     fn phase_change(&self) -> Option<MirPhase> {
-        Some(MirPhase::GeneratorLowering)
+        Some(MirPhase::GeneratorsLowered)
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 3b2332a6e31..2fca498a125 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -49,6 +49,7 @@ pub mod cleanup_post_borrowck;
 mod const_debuginfo;
 mod const_goto;
 mod const_prop;
+mod const_prop_lint;
 mod coverage;
 mod deaggregator;
 mod deduplicate_blocks;
@@ -341,7 +342,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -
             pm::run_passes(
                 tcx,
                 &mut body,
-                &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimization)],
+                &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimized)],
             );
         }
     }
@@ -398,7 +399,7 @@ fn mir_drops_elaborated_and_const_checked<'tcx>(
     }
 
     run_post_borrowck_cleanup_passes(tcx, &mut body);
-    assert!(body.phase == MirPhase::DropLowering);
+    assert!(body.phase == MirPhase::Deaggregated);
     tcx.alloc_steal_mir(body)
 }
 
@@ -430,6 +431,7 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc
         // `Deaggregator` is conceptually part of MIR building, some backends rely on it happening
         // and it can help optimizations.
         &deaggregator::Deaggregator,
+        &Lint(const_prop_lint::ConstProp),
     ];
 
     pm::run_passes(tcx, body, post_borrowck_cleanup);
@@ -458,7 +460,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         ],
     );
 
-    assert!(body.phase == MirPhase::GeneratorLowering);
+    assert!(body.phase == MirPhase::GeneratorsLowered);
 
     // The main optimizations that we do on MIR.
     pm::run_passes(
@@ -495,7 +497,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
             &deduplicate_blocks::DeduplicateBlocks,
             // Some cleanup necessary at least for LLVM and potentially other codegen backends.
             &add_call_guards::CriticalCallEdges,
-            &marker::PhaseChange(MirPhase::Optimization),
+            &marker::PhaseChange(MirPhase::Optimized),
             // Dump the end result for testing and debugging purposes.
             &dump_mir::Marker("PreCodegen"),
         ],
diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs
index 8725eae8709..740a2168b41 100644
--- a/compiler/rustc_mir_transform/src/pass_manager.rs
+++ b/compiler/rustc_mir_transform/src/pass_manager.rs
@@ -114,7 +114,7 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn
         }
     }
 
-    if validate || body.phase == MirPhase::Optimization {
+    if validate || body.phase == MirPhase::Optimized {
         validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase));
     }
 }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 2c3ddae9cb4..34218e87b51 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -9,7 +9,7 @@ use crate::def_collector::collect_definitions;
 use crate::imports::{Import, ImportKind};
 use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
 use crate::Namespace::{self, MacroNS, TypeNS, ValueNS};
-use crate::{CrateLint, Determinacy, ExternPreludeEntry, Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot};
 use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PerNS, ResolutionError};
 use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, VisResolutionError};
 
@@ -235,16 +235,16 @@ impl<'a> AsMut<Resolver<'a>> for BuildReducedGraphVisitor<'a, '_> {
 
 impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
     fn resolve_visibility(&mut self, vis: &ast::Visibility) -> ty::Visibility {
-        self.resolve_visibility_speculative(vis, false).unwrap_or_else(|err| {
+        self.try_resolve_visibility(vis, true).unwrap_or_else(|err| {
             self.r.report_vis_error(err);
             ty::Visibility::Public
         })
     }
 
-    fn resolve_visibility_speculative<'ast>(
+    fn try_resolve_visibility<'ast>(
         &mut self,
         vis: &'ast ast::Visibility,
-        speculative: bool,
+        finalize: bool,
     ) -> Result<ty::Visibility, VisResolutionError<'ast>> {
         let parent_scope = &self.parent_scope;
         match vis.kind {
@@ -296,13 +296,11 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                     &segments,
                     Some(TypeNS),
                     parent_scope,
-                    !speculative,
-                    path.span,
-                    CrateLint::SimplePath(id),
+                    if finalize { Finalize::SimplePath(id, path.span) } else { Finalize::No },
                 ) {
                     PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
                         let res = module.res().expect("visibility resolved to unnamed block");
-                        if !speculative {
+                        if finalize {
                             self.r.record_partial_res(id, PartialRes::new(res));
                         }
                         if module.is_normal() {
@@ -772,7 +770,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                         // correct visibilities for unnamed field placeholders specifically, so the
                         // constructor visibility should still be determined correctly.
                         let field_vis = self
-                            .resolve_visibility_speculative(&field.vis, true)
+                            .try_resolve_visibility(&field.vis, false)
                             .unwrap_or(ty::Visibility::Public);
                         if ctor_vis.is_at_least(field_vis, &*self.r) {
                             ctor_vis = field_vis;
@@ -1131,8 +1129,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                     ident,
                     MacroNS,
                     &self.parent_scope,
-                    false,
-                    ident.span,
+                    None,
                 );
                 if let Ok(binding) = result {
                     let import = macro_use_import(self, ident.span);
@@ -1272,9 +1269,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
             let vis = match item.kind {
                 // Visibilities must not be resolved non-speculatively twice
                 // and we already resolved this one as a `fn` item visibility.
-                ItemKind::Fn(..) => self
-                    .resolve_visibility_speculative(&item.vis, true)
-                    .unwrap_or(ty::Visibility::Public),
+                ItemKind::Fn(..) => {
+                    self.try_resolve_visibility(&item.vis, false).unwrap_or(ty::Visibility::Public)
+                }
                 _ => self.resolve_visibility(&item.vis),
             };
             if vis != ty::Visibility::Public {
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index e88b6a57376..d1685006881 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -24,10 +24,8 @@ use tracing::debug;
 use crate::imports::{Import, ImportKind, ImportResolver};
 use crate::path_names_to_string;
 use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind};
-use crate::{
-    BindingError, CrateLint, HasGenericParams, MacroRulesScope, Module, ModuleOrUniformRoot,
-};
-use crate::{NameBinding, NameBindingKind, PrivacyError, VisResolutionError};
+use crate::{BindingError, HasGenericParams, MacroRulesScope, Module, ModuleOrUniformRoot};
+use crate::{Finalize, NameBinding, NameBindingKind, PrivacyError, VisResolutionError};
 use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet, Segment};
 
 type Res = def::Res<ast::NodeId>;
@@ -1076,9 +1074,8 @@ impl<'a> Resolver<'a> {
                 ident,
                 ScopeSet::All(ns, false),
                 &parent_scope,
+                None,
                 false,
-                false,
-                ident.span,
             ) {
                 let desc = match binding.res() {
                     Res::Def(DefKind::Macro(MacroKind::Bang), _) => {
@@ -1405,10 +1402,10 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
             _ => return None,
         }
 
-        self.make_missing_self_suggestion(span, path.clone(), parent_scope)
-            .or_else(|| self.make_missing_crate_suggestion(span, path.clone(), parent_scope))
-            .or_else(|| self.make_missing_super_suggestion(span, path.clone(), parent_scope))
-            .or_else(|| self.make_external_crate_suggestion(span, path, parent_scope))
+        self.make_missing_self_suggestion(path.clone(), parent_scope)
+            .or_else(|| self.make_missing_crate_suggestion(path.clone(), parent_scope))
+            .or_else(|| self.make_missing_super_suggestion(path.clone(), parent_scope))
+            .or_else(|| self.make_external_crate_suggestion(path, parent_scope))
     }
 
     /// Suggest a missing `self::` if that resolves to an correct module.
@@ -1420,13 +1417,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
     /// ```
     fn make_missing_self_suggestion(
         &mut self,
-        span: Span,
         mut path: Vec<Segment>,
         parent_scope: &ParentScope<'b>,
     ) -> Option<(Vec<Segment>, Vec<String>)> {
         // Replace first ident with `self` and check if that is valid.
         path[0].ident.name = kw::SelfLower;
-        let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
+        let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No);
         debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result);
         if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None }
     }
@@ -1440,13 +1436,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
     /// ```
     fn make_missing_crate_suggestion(
         &mut self,
-        span: Span,
         mut path: Vec<Segment>,
         parent_scope: &ParentScope<'b>,
     ) -> Option<(Vec<Segment>, Vec<String>)> {
         // Replace first ident with `crate` and check if that is valid.
         path[0].ident.name = kw::Crate;
-        let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
+        let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No);
         debug!("make_missing_crate_suggestion:  path={:?} result={:?}", path, result);
         if let PathResult::Module(..) = result {
             Some((
@@ -1472,13 +1467,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
     /// ```
     fn make_missing_super_suggestion(
         &mut self,
-        span: Span,
         mut path: Vec<Segment>,
         parent_scope: &ParentScope<'b>,
     ) -> Option<(Vec<Segment>, Vec<String>)> {
         // Replace first ident with `crate` and check if that is valid.
         path[0].ident.name = kw::Super;
-        let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
+        let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No);
         debug!("make_missing_super_suggestion:  path={:?} result={:?}", path, result);
         if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None }
     }
@@ -1495,7 +1489,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
     /// name as the first part of path.
     fn make_external_crate_suggestion(
         &mut self,
-        span: Span,
         mut path: Vec<Segment>,
         parent_scope: &ParentScope<'b>,
     ) -> Option<(Vec<Segment>, Vec<String>)> {
@@ -1513,7 +1506,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
         for name in extern_crate_names.into_iter() {
             // Replace first ident with a crate name and check if that is valid.
             path[0].ident.name = name;
-            let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
+            let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No);
             debug!(
                 "make_external_crate_suggestion: name={:?} path={:?} result={:?}",
                 name, path, result
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 70ade7a5600..4f0dad13b5b 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -6,7 +6,7 @@ use crate::Namespace::{self, MacroNS, TypeNS};
 use crate::{module_to_string, names_to_string};
 use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind};
 use crate::{BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
-use crate::{CrateLint, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet, Weak};
+use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet, Weak};
 use crate::{NameBinding, NameBindingKind, PathResult, PrivacyError, ToNameBinding};
 
 use rustc_ast::NodeId;
@@ -123,10 +123,6 @@ impl<'a> Import<'a> {
             _ => false,
         }
     }
-
-    crate fn crate_lint(&self) -> CrateLint {
-        CrateLint::UsePath { root_id: self.root_id, root_span: self.root_span }
-    }
 }
 
 #[derive(Clone, Default, Debug)]
@@ -179,8 +175,7 @@ impl<'a> Resolver<'a> {
         ident: Ident,
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
-        record_used: bool,
-        path_span: Span,
+        finalize: Option<Span>,
     ) -> Result<&'a NameBinding<'a>, Determinacy> {
         self.resolve_ident_in_module_unadjusted_ext(
             module,
@@ -188,14 +183,13 @@ impl<'a> Resolver<'a> {
             ns,
             parent_scope,
             false,
-            record_used,
-            path_span,
+            finalize,
         )
         .map_err(|(determinacy, _)| determinacy)
     }
 
     /// Attempts to resolve `ident` in namespaces `ns` of `module`.
-    /// Invariant: if `record_used` is `Some`, expansion and import resolution must be complete.
+    /// Invariant: if `finalize` is `Some`, expansion and import resolution must be complete.
     crate fn resolve_ident_in_module_unadjusted_ext(
         &mut self,
         module: ModuleOrUniformRoot<'a>,
@@ -203,8 +197,7 @@ impl<'a> Resolver<'a> {
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
         restricted_shadowing: bool,
-        record_used: bool,
-        path_span: Span,
+        finalize: Option<Span>,
     ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
         let module = match module {
             ModuleOrUniformRoot::Module(module) => module,
@@ -214,9 +207,8 @@ impl<'a> Resolver<'a> {
                     ident,
                     ScopeSet::AbsolutePath(ns),
                     parent_scope,
-                    record_used,
-                    record_used,
-                    path_span,
+                    finalize,
+                    finalize.is_some(),
                 );
                 return binding.map_err(|determinacy| (determinacy, Weak::No));
             }
@@ -224,7 +216,7 @@ impl<'a> Resolver<'a> {
                 assert!(!restricted_shadowing);
                 return if ns != TypeNS {
                     Err((Determined, Weak::No))
-                } else if let Some(binding) = self.extern_prelude_get(ident, !record_used) {
+                } else if let Some(binding) = self.extern_prelude_get(ident, finalize.is_some()) {
                     Ok(binding)
                 } else if !self.graph_root.unexpanded_invocations.borrow().is_empty() {
                     // Macro-expanded `extern crate` items can add names to extern prelude.
@@ -254,9 +246,8 @@ impl<'a> Resolver<'a> {
                     ident,
                     scopes,
                     parent_scope,
-                    record_used,
-                    record_used,
-                    path_span,
+                    finalize,
+                    finalize.is_some(),
                 );
                 return binding.map_err(|determinacy| (determinacy, Weak::No));
             }
@@ -266,7 +257,7 @@ impl<'a> Resolver<'a> {
         let resolution =
             self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports.
 
-        if let Some(binding) = resolution.binding {
+        if let Some(binding) = resolution.binding && let Some(path_span) = finalize {
             if !restricted_shadowing && binding.expansion != LocalExpnId::ROOT {
                 if let NameBindingKind::Res(_, true) = binding.kind {
                     self.macro_expanded_macro_export_errors.insert((path_span, binding.span));
@@ -284,7 +275,7 @@ impl<'a> Resolver<'a> {
             if usable { Ok(binding) } else { Err((Determined, Weak::No)) }
         };
 
-        if record_used {
+        if let Some(path_span) = finalize {
             return resolution
                 .binding
                 .and_then(|binding| {
@@ -357,14 +348,8 @@ impl<'a> Resolver<'a> {
             let ImportKind::Single { source: ident, .. } = single_import.kind else {
                 unreachable!();
             };
-            match self.resolve_ident_in_module(
-                module,
-                ident,
-                ns,
-                &single_import.parent_scope,
-                false,
-                path_span,
-            ) {
+            match self.resolve_ident_in_module(module, ident, ns, &single_import.parent_scope, None)
+            {
                 Err(Determined) => continue,
                 Ok(binding)
                     if !self.is_accessible_from(binding.vis, single_import.parent_scope.module) =>
@@ -438,8 +423,7 @@ impl<'a> Resolver<'a> {
                 ident,
                 ns,
                 adjusted_parent_scope,
-                false,
-                path_span,
+                None,
             );
 
             match result {
@@ -787,14 +771,8 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
             // For better failure detection, pretend that the import will
             // not define any names while resolving its module path.
             let orig_vis = import.vis.replace(ty::Visibility::Invisible);
-            let path_res = self.r.resolve_path(
-                &import.module_path,
-                None,
-                &import.parent_scope,
-                false,
-                import.span,
-                import.crate_lint(),
-            );
+            let path_res =
+                self.r.resolve_path(&import.module_path, None, &import.parent_scope, Finalize::No);
             import.vis.set(orig_vis);
 
             match path_res {
@@ -833,8 +811,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                         source,
                         ns,
                         &import.parent_scope,
-                        false,
-                        import.span,
+                        None,
                     );
                     import.vis.set(orig_vis);
                     source_bindings[ns].set(binding);
@@ -887,14 +864,13 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
             _ => None,
         };
         let prev_ambiguity_errors_len = self.r.ambiguity_errors.len();
-        let path_res = self.r.resolve_path(
-            &import.module_path,
-            None,
-            &import.parent_scope,
-            true,
-            import.span,
-            import.crate_lint(),
-        );
+        let finalize = Finalize::UsePath {
+            root_id: import.root_id,
+            root_span: import.root_span,
+            path_span: import.span,
+        };
+        let path_res =
+            self.r.resolve_path(&import.module_path, None, &import.parent_scope, finalize);
         let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
         if let Some(orig_unusable_binding) = orig_unusable_binding {
             self.r.unusable_binding = orig_unusable_binding;
@@ -981,12 +957,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                     // 2 segments, so the `resolve_path` above won't trigger it.
                     let mut full_path = import.module_path.clone();
                     full_path.push(Segment::from_ident(Ident::empty()));
-                    self.r.lint_if_path_starts_with_module(
-                        import.crate_lint(),
-                        &full_path,
-                        import.span,
-                        None,
-                    );
+                    self.r.lint_if_path_starts_with_module(finalize, &full_path, None);
                 }
 
                 if let ModuleOrUniformRoot::Module(module) = module {
@@ -1024,8 +995,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                     ident,
                     ns,
                     &import.parent_scope,
-                    true,
-                    import.span,
+                    Some(import.span),
                 );
                 this.last_import_segment = orig_last_import_segment;
                 this.unusable_binding = orig_unusable_binding;
@@ -1086,8 +1056,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                         ident,
                         ns,
                         &import.parent_scope,
-                        true,
-                        import.span,
+                        Some(import.span),
                     );
                     if binding.is_ok() {
                         all_ns_failed = false;
@@ -1253,12 +1222,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
             full_path.push(Segment::from_ident(ident));
             self.r.per_ns(|this, ns| {
                 if let Ok(binding) = source_bindings[ns].get() {
-                    this.lint_if_path_starts_with_module(
-                        import.crate_lint(),
-                        &full_path,
-                        import.span,
-                        Some(binding),
-                    );
+                    this.lint_if_path_starts_with_module(finalize, &full_path, Some(binding));
                 }
             });
         }
@@ -1314,9 +1278,8 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                     target,
                     ScopeSet::All(ns, false),
                     &import.parent_scope,
+                    None,
                     false,
-                    false,
-                    import.span,
                 ) {
                     Ok(other_binding) => {
                         is_redundant[ns] = Some(
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index d5b1aa00e52..bb05a3d7510 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -7,7 +7,7 @@
 
 use RibKind::*;
 
-use crate::{path_names_to_string, BindingError, CrateLint, LexicalScopeBinding};
+use crate::{path_names_to_string, BindingError, Finalize, LexicalScopeBinding};
 use crate::{Module, ModuleOrUniformRoot, ParentScope, PathResult};
 use crate::{ResolutionError, Resolver, Segment, UseError};
 
@@ -483,7 +483,11 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
             TyKind::ImplicitSelf => {
                 let self_ty = Ident::with_dummy_span(kw::SelfUpper);
                 let res = self
-                    .resolve_ident_in_lexical_scope(self_ty, TypeNS, Some(ty.id), ty.span)
+                    .resolve_ident_in_lexical_scope(
+                        self_ty,
+                        TypeNS,
+                        Finalize::SimplePath(ty.id, ty.span),
+                    )
                     .map_or(Res::Err, |d| d.res());
                 self.r.record_partial_res(ty.id, PartialRes::new(res));
             }
@@ -675,8 +679,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                             self.resolve_ident_in_lexical_scope(
                                 path.segments[0].ident,
                                 ns,
-                                None,
-                                path.span,
+                                Finalize::No,
                             )
                             .is_some()
                         };
@@ -751,15 +754,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         &mut self,
         ident: Ident,
         ns: Namespace,
-        record_used_id: Option<NodeId>,
-        path_span: Span,
+        finalize: Finalize,
     ) -> Option<LexicalScopeBinding<'a>> {
         self.r.resolve_ident_in_lexical_scope(
             ident,
             ns,
             &self.parent_scope,
-            record_used_id,
-            path_span,
+            finalize,
             &self.ribs[ns],
         )
     }
@@ -768,19 +769,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         &mut self,
         path: &[Segment],
         opt_ns: Option<Namespace>, // `None` indicates a module path in import
-        record_used: bool,
-        path_span: Span,
-        crate_lint: CrateLint,
+        finalize: Finalize,
     ) -> PathResult<'a> {
-        self.r.resolve_path_with_ribs(
-            path,
-            opt_ns,
-            &self.parent_scope,
-            record_used,
-            path_span,
-            crate_lint,
-            Some(&self.ribs),
-        )
+        self.r.resolve_path_with_ribs(path, opt_ns, &self.parent_scope, finalize, Some(&self.ribs))
     }
 
     // AST resolution
@@ -943,15 +934,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             };
 
             for &ns in nss {
-                match self.resolve_ident_in_lexical_scope(ident, ns, None, use_tree.prefix.span) {
+                match self.resolve_ident_in_lexical_scope(ident, ns, Finalize::No) {
                     Some(LexicalScopeBinding::Res(..)) => {
                         report_error(self, ns);
                     }
                     Some(LexicalScopeBinding::Item(binding)) => {
                         let orig_unusable_binding =
                             replace(&mut self.r.unusable_binding, Some(binding));
-                        if let Some(LexicalScopeBinding::Res(..)) = self
-                            .resolve_ident_in_lexical_scope(ident, ns, None, use_tree.prefix.span)
+                        if let Some(LexicalScopeBinding::Res(..)) =
+                            self.resolve_ident_in_lexical_scope(ident, ns, Finalize::No)
                         {
                             report_error(self, ns);
                         }
@@ -1246,25 +1237,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         if let Some(trait_ref) = opt_trait_ref {
             let path: Vec<_> = Segment::from_path(&trait_ref.path);
             let res = self.smart_resolve_path_fragment(
-                trait_ref.ref_id,
                 None,
                 &path,
-                trait_ref.path.span,
                 PathSource::Trait(AliasPossibility::No),
-                CrateLint::SimplePath(trait_ref.ref_id),
+                Finalize::SimplePath(trait_ref.ref_id, trait_ref.path.span),
             );
-            let res = res.base_res();
-            if res != Res::Err {
-                if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(
-                    &path,
-                    Some(TypeNS),
-                    true,
-                    trait_ref.path.span,
-                    CrateLint::SimplePath(trait_ref.ref_id),
-                ) {
-                    new_id = Some(res.def_id());
-                    new_val = Some((module, trait_ref.clone()));
-                }
+            if let Some(def_id) = res.base_res().opt_def_id() {
+                new_id = Some(def_id);
+                new_val = Some((self.r.expect_module(def_id), trait_ref.clone()));
             }
         }
         let original_trait_ref = replace(&mut self.current_trait_ref, new_val);
@@ -1702,7 +1682,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     // then fall back to a fresh binding.
                     let has_sub = sub.is_some();
                     let res = self
-                        .try_resolve_as_non_binding(pat_src, pat, bmode, ident, has_sub)
+                        .try_resolve_as_non_binding(pat_src, bmode, ident, has_sub)
                         .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings));
                     self.r.record_partial_res(pat.id, PartialRes::new(res));
                     self.r.record_pat_span(pat.id, pat.span);
@@ -1813,7 +1793,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     fn try_resolve_as_non_binding(
         &mut self,
         pat_src: PatternSource,
-        pat: &Pat,
         bm: BindingMode,
         ident: Ident,
         has_sub: bool,
@@ -1823,7 +1802,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         // also be interpreted as a path to e.g. a constant, variant, etc.
         let is_syntactic_ambiguity = !has_sub && bm == BindingMode::ByValue(Mutability::Not);
 
-        let ls_binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, None, pat.span)?;
+        let ls_binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, Finalize::No)?;
         let (res, binding) = match ls_binding {
             LexicalScopeBinding::Item(binding)
                 if is_syntactic_ambiguity && binding.is_ambiguity() =>
@@ -1912,35 +1891,34 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         source: PathSource<'ast>,
     ) {
         self.smart_resolve_path_fragment(
-            id,
             qself,
             &Segment::from_path(path),
-            path.span,
             source,
-            CrateLint::SimplePath(id),
+            Finalize::SimplePath(id, path.span),
         );
     }
 
     fn smart_resolve_path_fragment(
         &mut self,
-        id: NodeId,
         qself: Option<&QSelf>,
         path: &[Segment],
-        span: Span,
         source: PathSource<'ast>,
-        crate_lint: CrateLint,
+        finalize: Finalize,
     ) -> PartialRes {
         tracing::debug!(
-            "smart_resolve_path_fragment(id={:?}, qself={:?}, path={:?})",
-            id,
+            "smart_resolve_path_fragment(qself={:?}, path={:?}, finalize={:?})",
             qself,
-            path
+            path,
+            finalize,
         );
         let ns = source.namespace();
 
+        let (id, path_span) =
+            finalize.node_id_and_path_span().expect("unexpected speculative resolution");
         let report_errors = |this: &mut Self, res: Option<Res>| {
             if this.should_report_errs() {
-                let (err, candidates) = this.smart_resolve_report_errors(path, span, source, res);
+                let (err, candidates) =
+                    this.smart_resolve_report_errors(path, path_span, source, res);
 
                 let def_id = this.parent_scope.module.nearest_parent_mod();
                 let instead = res.is_some();
@@ -1978,7 +1956,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             };
 
             let (mut err, candidates) =
-                this.smart_resolve_report_errors(path, span, PathSource::Type, None);
+                this.smart_resolve_report_errors(path, path_span, PathSource::Type, None);
 
             if candidates.is_empty() {
                 err.cancel();
@@ -2027,13 +2005,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         };
 
         let partial_res = match self.resolve_qpath_anywhere(
-            id,
             qself,
             path,
             ns,
-            span,
+            path_span,
             source.defer_to_typeck(),
-            crate_lint,
+            finalize,
         ) {
             Ok(Some(partial_res)) if partial_res.unresolved_segments() == 0 => {
                 if source.is_expected(partial_res.base_res()) || partial_res.base_res() == Res::Err
@@ -2060,14 +2037,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     std_path.push(Segment::from_ident(Ident::with_dummy_span(sym::std)));
                     std_path.extend(path);
                     if let PathResult::Module(_) | PathResult::NonModule(_) =
-                        self.resolve_path(&std_path, Some(ns), false, span, CrateLint::No)
+                        self.resolve_path(&std_path, Some(ns), Finalize::No)
                     {
                         // Check if we wrote `str::from_utf8` instead of `std::str::from_utf8`
                         let item_span =
-                            path.iter().last().map_or(span, |segment| segment.ident.span);
+                            path.iter().last().map_or(path_span, |segment| segment.ident.span);
 
-                        self.r.confused_type_with_std_module.insert(item_span, span);
-                        self.r.confused_type_with_std_module.insert(span, span);
+                        self.r.confused_type_with_std_module.insert(item_span, path_span);
+                        self.r.confused_type_with_std_module.insert(path_span, path_span);
                     }
                 }
 
@@ -2093,19 +2070,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         partial_res
     }
 
-    fn self_type_is_available(&mut self, span: Span) -> bool {
+    fn self_type_is_available(&mut self) -> bool {
         let binding = self.resolve_ident_in_lexical_scope(
             Ident::with_dummy_span(kw::SelfUpper),
             TypeNS,
-            None,
-            span,
+            Finalize::No,
         );
         if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false }
     }
 
-    fn self_value_is_available(&mut self, self_span: Span, path_span: Span) -> bool {
+    fn self_value_is_available(&mut self, self_span: Span) -> bool {
         let ident = Ident::new(kw::SelfLower, self_span);
-        let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, None, path_span);
+        let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, Finalize::No);
         if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false }
     }
 
@@ -2127,19 +2103,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     // Resolve in alternative namespaces if resolution in the primary namespace fails.
     fn resolve_qpath_anywhere(
         &mut self,
-        id: NodeId,
         qself: Option<&QSelf>,
         path: &[Segment],
         primary_ns: Namespace,
         span: Span,
         defer_to_typeck: bool,
-        crate_lint: CrateLint,
+        finalize: Finalize,
     ) -> Result<Option<PartialRes>, Spanned<ResolutionError<'a>>> {
         let mut fin_res = None;
 
         for (i, &ns) in [primary_ns, TypeNS, ValueNS].iter().enumerate() {
             if i == 0 || ns != primary_ns {
-                match self.resolve_qpath(id, qself, path, ns, span, crate_lint)? {
+                match self.resolve_qpath(qself, path, ns, finalize)? {
                     Some(partial_res)
                         if partial_res.unresolved_segments() == 0 || defer_to_typeck =>
                     {
@@ -2172,16 +2147,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     /// Handles paths that may refer to associated items.
     fn resolve_qpath(
         &mut self,
-        id: NodeId,
         qself: Option<&QSelf>,
         path: &[Segment],
         ns: Namespace,
-        span: Span,
-        crate_lint: CrateLint,
+        finalize: Finalize,
     ) -> Result<Option<PartialRes>, Spanned<ResolutionError<'a>>> {
         debug!(
-            "resolve_qpath(id={:?}, qself={:?}, path={:?}, ns={:?}, span={:?})",
-            id, qself, path, ns, span,
+            "resolve_qpath(qself={:?}, path={:?}, ns={:?}, finalize={:?})",
+            qself, path, ns, finalize,
         );
 
         if let Some(qself) = qself {
@@ -2208,15 +2181,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             // *actually* appears, so for the purposes of the crate
             // lint we pass along information that this is the trait
             // name from a fully qualified path, and this also
-            // contains the full span (the `CrateLint::QPathTrait`).
+            // contains the full span (the `Finalize::QPathTrait`).
             let ns = if qself.position + 1 == path.len() { ns } else { TypeNS };
             let partial_res = self.smart_resolve_path_fragment(
-                id,
                 None,
                 &path[..=qself.position],
-                span,
                 PathSource::TraitItem(ns),
-                CrateLint::QPathTrait { qpath_id: id, qpath_span: qself.path_span },
+                finalize.node_id_and_path_span().map_or(Finalize::No, |(qpath_id, path_span)| {
+                    Finalize::QPathTrait { qpath_id, qpath_span: qself.path_span, path_span }
+                }),
             );
 
             // The remaining segments (the `C` in our example) will
@@ -2228,7 +2201,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             )));
         }
 
-        let result = match self.resolve_path(&path, Some(ns), true, span, crate_lint) {
+        let result = match self.resolve_path(&path, Some(ns), finalize) {
             PathResult::NonModule(path_res) => path_res,
             PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => {
                 PartialRes::new(module.res().unwrap())
@@ -2266,15 +2239,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             && result.base_res() != Res::Err
             && path[0].ident.name != kw::PathRoot
             && path[0].ident.name != kw::DollarCrate
+            && let Some((id, path_span)) = finalize.node_id_and_path_span()
         {
             let unqualified_result = {
-                match self.resolve_path(
-                    &[*path.last().unwrap()],
-                    Some(ns),
-                    false,
-                    span,
-                    CrateLint::No,
-                ) {
+                match self.resolve_path(&[*path.last().unwrap()], Some(ns), Finalize::No) {
                     PathResult::NonModule(path_res) => path_res.base_res(),
                     PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
                         module.res().unwrap()
@@ -2284,7 +2252,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             };
             if result.base_res() == unqualified_result {
                 let lint = lint::builtin::UNUSED_QUALIFICATIONS;
-                self.r.lint_buffer.buffer_lint(lint, id, span, "unnecessary qualification")
+                self.r.lint_buffer.buffer_lint(lint, id, path_span, "unnecessary qualification")
             }
         }
 
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 1394f4083d0..038ba220608 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -2,7 +2,7 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
 use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext};
 use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
 use crate::path_names_to_string;
-use crate::{CrateLint, Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{Finalize, Module, ModuleKind, ModuleOrUniformRoot};
 use crate::{PathResult, PathSource, Segment};
 
 use rustc_ast::visit::FnKind;
@@ -187,12 +187,11 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 (String::new(), "the crate root".to_string())
             } else {
                 let mod_path = &path[..path.len() - 1];
-                let mod_prefix =
-                    match self.resolve_path(mod_path, Some(TypeNS), false, span, CrateLint::No) {
-                        PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),
-                        _ => None,
-                    }
-                    .map_or_else(String::new, |res| format!("{} ", res.descr()));
+                let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), Finalize::No) {
+                    PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),
+                    _ => None,
+                }
+                .map_or_else(String::new, |res| format!("{} ", res.descr()));
                 (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)))
             };
             (
@@ -232,7 +231,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             _ => {}
         }
 
-        let is_assoc_fn = self.self_type_is_available(span);
+        let is_assoc_fn = self.self_type_is_available();
         // Emit help message for fake-self from other languages (e.g., `this` in Javascript).
         if ["this", "my"].contains(&item_str.as_str()) && is_assoc_fn {
             err.span_suggestion_short(
@@ -241,7 +240,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 "self".to_string(),
                 Applicability::MaybeIncorrect,
             );
-            if !self.self_value_is_available(path[0].ident.span, span) {
+            if !self.self_value_is_available(path[0].ident.span) {
                 if let Some((FnKind::Fn(_, _, sig, ..), fn_span)) =
                     &self.diagnostic_metadata.current_function
                 {
@@ -402,9 +401,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 );
             }
         }
-        if path.len() == 1 && self.self_type_is_available(span) {
+        if path.len() == 1 && self.self_type_is_available() {
             if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) {
-                let self_is_available = self.self_value_is_available(path[0].ident.span, span);
+                let self_is_available = self.self_value_is_available(path[0].ident.span);
                 match candidate {
                     AssocSuggestion::Field => {
                         if self_is_available {
@@ -461,7 +460,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         }
 
         // Try Levenshtein algorithm.
-        let typo_sugg = self.lookup_typo_candidate(path, ns, is_expected, span);
+        let typo_sugg = self.lookup_typo_candidate(path, ns, is_expected);
         // Try context-dependent help if relaxed lookup didn't work.
         if let Some(res) = res {
             if self.smart_resolve_context_dependent_help(
@@ -562,7 +561,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 }
 
                 // If the trait has a single item (which wasn't matched by Levenshtein), suggest it
-                let suggestion = self.get_single_associated_item(&path, span, &source, is_expected);
+                let suggestion = self.get_single_associated_item(&path, &source, is_expected);
                 self.r.add_typo_suggestion(&mut err, suggestion, ident_span);
             }
             if fallback {
@@ -641,14 +640,13 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
     fn get_single_associated_item(
         &mut self,
         path: &[Segment],
-        span: Span,
         source: &PathSource<'_>,
         filter_fn: &impl Fn(Res) -> bool,
     ) -> Option<TypoSuggestion> {
         if let crate::PathSource::TraitItem(_) = source {
             let mod_path = &path[..path.len() - 1];
             if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
-                self.resolve_path(mod_path, None, false, span, CrateLint::No)
+                self.resolve_path(mod_path, None, Finalize::No)
             {
                 let resolutions = self.r.resolutions(module).borrow();
                 let targets: Vec<_> =
@@ -699,13 +697,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         {
             // use this to verify that ident is a type param.
             let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
-                bounded_ty.id,
                 None,
                 &Segment::from_path(path),
                 Namespace::TypeNS,
                 span,
                 true,
-                CrateLint::No,
+                Finalize::No,
             ) else {
                 return false;
             };
@@ -724,13 +721,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         if let ast::TyKind::Path(None, type_param_path) = &ty.peel_refs().kind {
             // Confirm that the `SelfTy` is a type parameter.
             let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
-                bounded_ty.id,
                 None,
                 &Segment::from_path(type_param_path),
                 Namespace::TypeNS,
                 span,
                 true,
-                CrateLint::No,
+                Finalize::No,
             ) else {
                 return false;
             };
@@ -1292,8 +1288,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 ident,
                 ns,
                 &self.parent_scope,
-                false,
-                module.span,
+                None,
             ) {
                 let res = binding.res();
                 if filter_fn(res) {
@@ -1323,7 +1318,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         path: &[Segment],
         ns: Namespace,
         filter_fn: &impl Fn(Res) -> bool,
-        span: Span,
     ) -> Option<TypoSuggestion> {
         let mut names = Vec::new();
         if path.len() == 1 {
@@ -1384,7 +1378,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             // Search in module.
             let mod_path = &path[..path.len() - 1];
             if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
-                self.resolve_path(mod_path, Some(TypeNS), false, span, CrateLint::No)
+                self.resolve_path(mod_path, Some(TypeNS), Finalize::No)
             {
                 self.r.add_module_candidates(module, &mut names, &filter_fn);
             }
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 77fd7115938..19eeae4cf23 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -13,6 +13,7 @@
 #![feature(drain_filter)]
 #![feature(bool_to_option)]
 #![feature(crate_visibility_modifier)]
+#![feature(let_chains)]
 #![feature(let_else)]
 #![feature(never_type)]
 #![feature(nll)]
@@ -54,9 +55,9 @@ use rustc_index::vec::IndexVec;
 use rustc_metadata::creader::{CStore, CrateLoader};
 use rustc_middle::metadata::ModChild;
 use rustc_middle::middle::privacy::AccessLevels;
-use rustc_middle::span_bug;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, DefIdTree, MainDefinition, RegisteredTools, ResolverOutputs};
+use rustc_middle::{bug, span_bug};
 use rustc_query_system::ich::StableHashingContext;
 use rustc_session::cstore::{CrateStore, MetadataLoaderDyn};
 use rustc_session::lint;
@@ -71,7 +72,7 @@ use rustc_span::{Span, DUMMY_SP};
 use smallvec::{smallvec, SmallVec};
 use std::cell::{Cell, RefCell};
 use std::collections::BTreeSet;
-use std::{cmp, fmt, iter, mem, ptr};
+use std::{cmp, fmt, mem, ptr};
 use tracing::debug;
 
 use diagnostics::{extend_span_to_previous_binding, find_span_of_binding_until_next_binding};
@@ -449,6 +450,19 @@ enum PathResult<'a> {
     },
 }
 
+impl<'a> PathResult<'a> {
+    fn failed(
+        span: Span,
+        is_error_from_last_segment: bool,
+        finalize: bool,
+        label_and_suggestion: impl FnOnce() -> (String, Option<Suggestion>),
+    ) -> PathResult<'a> {
+        let (label, suggestion) =
+            if finalize { label_and_suggestion() } else { (String::new(), None) };
+        PathResult::Failed { span, label, suggestion, is_error_from_last_segment }
+    }
+}
+
 #[derive(Debug)]
 enum ModuleKind {
     /// An anonymous module; e.g., just a block.
@@ -1936,8 +1950,7 @@ impl<'a> Resolver<'a> {
         mut ident: Ident,
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
-        record_used_id: Option<NodeId>,
-        path_span: Span,
+        finalize_full: Finalize,
         ribs: &[Rib<'a>],
     ) -> Option<LexicalScopeBinding<'a>> {
         assert!(ns == TypeNS || ns == ValueNS);
@@ -1959,7 +1972,7 @@ impl<'a> Resolver<'a> {
         let normalized_ident = Ident { span: normalized_span, ..ident };
 
         // Walk backwards up the ribs in scope.
-        let record_used = record_used_id.is_some();
+        let finalize = finalize_full.path_span();
         let mut module = self.graph_root;
         for i in (0..ribs.len()).rev() {
             debug!("walk rib\n{:?}", ribs[i].bindings);
@@ -1973,8 +1986,7 @@ impl<'a> Resolver<'a> {
                     i,
                     rib_ident,
                     *res,
-                    record_used,
-                    path_span,
+                    finalize,
                     *original_rib_ident_def,
                     ribs,
                 )));
@@ -2001,8 +2013,7 @@ impl<'a> Resolver<'a> {
                 ident,
                 ns,
                 parent_scope,
-                record_used,
-                path_span,
+                finalize,
             );
             if let Ok(binding) = item {
                 // The ident resolves to an item.
@@ -2011,11 +2022,10 @@ impl<'a> Resolver<'a> {
         }
         self.early_resolve_ident_in_lexical_scope(
             orig_ident,
-            ScopeSet::Late(ns, module, record_used_id),
+            ScopeSet::Late(ns, module, finalize_full.node_id()),
             parent_scope,
-            record_used,
-            record_used,
-            path_span,
+            finalize,
+            finalize.is_some(),
         )
         .ok()
         .map(LexicalScopeBinding::Item)
@@ -2075,10 +2085,9 @@ impl<'a> Resolver<'a> {
         ident: Ident,
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
-        record_used: bool,
-        path_span: Span,
+        finalize: Option<Span>,
     ) -> Result<&'a NameBinding<'a>, Determinacy> {
-        self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, record_used, path_span)
+        self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize)
             .map_err(|(determinacy, _)| determinacy)
     }
 
@@ -2088,8 +2097,7 @@ impl<'a> Resolver<'a> {
         mut ident: Ident,
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
-        record_used: bool,
-        path_span: Span,
+        finalize: Option<Span>,
     ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
         let tmp_parent_scope;
         let mut adjusted_parent_scope = parent_scope;
@@ -2114,8 +2122,7 @@ impl<'a> Resolver<'a> {
             ns,
             adjusted_parent_scope,
             false,
-            record_used,
-            path_span,
+            finalize,
         )
     }
 
@@ -2206,19 +2213,9 @@ impl<'a> Resolver<'a> {
         path: &[Segment],
         opt_ns: Option<Namespace>, // `None` indicates a module path in import
         parent_scope: &ParentScope<'a>,
-        record_used: bool,
-        path_span: Span,
-        crate_lint: CrateLint,
+        finalize: Finalize,
     ) -> PathResult<'a> {
-        self.resolve_path_with_ribs(
-            path,
-            opt_ns,
-            parent_scope,
-            record_used,
-            path_span,
-            crate_lint,
-            None,
-        )
+        self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None)
     }
 
     fn resolve_path_with_ribs(
@@ -2226,25 +2223,20 @@ impl<'a> Resolver<'a> {
         path: &[Segment],
         opt_ns: Option<Namespace>, // `None` indicates a module path in import
         parent_scope: &ParentScope<'a>,
-        record_used: bool,
-        path_span: Span,
-        crate_lint: CrateLint,
+        finalize_full: Finalize,
         ribs: Option<&PerNS<Vec<Rib<'a>>>>,
     ) -> PathResult<'a> {
+        debug!("resolve_path(path={:?}, opt_ns={:?}, finalize={:?})", path, opt_ns, finalize_full);
+
+        let finalize = finalize_full.path_span();
         let mut module = None;
         let mut allow_super = true;
         let mut second_binding = None;
 
-        debug!(
-            "resolve_path(path={:?}, opt_ns={:?}, record_used={:?}, \
-             path_span={:?}, crate_lint={:?})",
-            path, opt_ns, record_used, path_span, crate_lint,
-        );
-
         for (i, &Segment { ident, id, has_generic_args: _ }) in path.iter().enumerate() {
             debug!("resolve_path ident {} {:?} {:?}", i, ident, id);
             let record_segment_res = |this: &mut Self, res| {
-                if record_used {
+                if finalize.is_some() {
                     if let Some(id) = id {
                         if !this.partial_res_map.contains_key(&id) {
                             assert!(id != ast::DUMMY_NODE_ID, "Trying to resolve dummy id");
@@ -2278,13 +2270,9 @@ impl<'a> Resolver<'a> {
                             continue;
                         }
                     }
-                    let msg = "there are too many leading `super` keywords".to_string();
-                    return PathResult::Failed {
-                        span: ident.span,
-                        label: msg,
-                        suggestion: None,
-                        is_error_from_last_segment: false,
-                    };
+                    return PathResult::failed(ident.span, false, finalize.is_some(), || {
+                        ("there are too many leading `super` keywords".to_string(), None)
+                    });
                 }
                 if i == 0 {
                     if name == kw::SelfLower {
@@ -2313,22 +2301,19 @@ impl<'a> Resolver<'a> {
 
             // Report special messages for path segment keywords in wrong positions.
             if ident.is_path_segment_keyword() && i != 0 {
-                let name_str = if name == kw::PathRoot {
-                    "crate root".to_string()
-                } else {
-                    format!("`{}`", name)
-                };
-                let label = if i == 1 && path[0].ident.name == kw::PathRoot {
-                    format!("global paths cannot start with {}", name_str)
-                } else {
-                    format!("{} in paths can only be used in start position", name_str)
-                };
-                return PathResult::Failed {
-                    span: ident.span,
-                    label,
-                    suggestion: None,
-                    is_error_from_last_segment: false,
-                };
+                return PathResult::failed(ident.span, false, finalize.is_some(), || {
+                    let name_str = if name == kw::PathRoot {
+                        "crate root".to_string()
+                    } else {
+                        format!("`{}`", name)
+                    };
+                    let label = if i == 1 && path[0].ident.name == kw::PathRoot {
+                        format!("global paths cannot start with {}", name_str)
+                    } else {
+                        format!("{} in paths can only be used in start position", name_str)
+                    };
+                    (label, None)
+                });
             }
 
             enum FindBindingResult<'a> {
@@ -2337,36 +2322,22 @@ impl<'a> Resolver<'a> {
             }
             let find_binding_in_ns = |this: &mut Self, ns| {
                 let binding = if let Some(module) = module {
-                    this.resolve_ident_in_module(
-                        module,
-                        ident,
-                        ns,
-                        parent_scope,
-                        record_used,
-                        path_span,
-                    )
+                    this.resolve_ident_in_module(module, ident, ns, parent_scope, finalize)
                 } else if ribs.is_none() || opt_ns.is_none() || opt_ns == Some(MacroNS) {
                     let scopes = ScopeSet::All(ns, opt_ns.is_none());
                     this.early_resolve_ident_in_lexical_scope(
                         ident,
                         scopes,
                         parent_scope,
-                        record_used,
-                        record_used,
-                        path_span,
+                        finalize,
+                        finalize.is_some(),
                     )
                 } else {
-                    let record_used_id = if record_used {
-                        crate_lint.node_id().or(Some(CRATE_NODE_ID))
-                    } else {
-                        None
-                    };
                     match this.resolve_ident_in_lexical_scope(
                         ident,
                         ns,
                         parent_scope,
-                        record_used_id,
-                        path_span,
+                        finalize_full,
                         &ribs.unwrap()[ns],
                     ) {
                         // we found a locally-imported or available item/module
@@ -2380,7 +2351,7 @@ impl<'a> Resolver<'a> {
                                 PartialRes::with_unresolved_segments(res, path.len() - 1),
                             ));
                         }
-                        _ => Err(Determinacy::determined(record_used)),
+                        _ => Err(Determinacy::determined(finalize.is_some())),
                     }
                 };
                 FindBindingResult::Binding(binding)
@@ -2414,30 +2385,20 @@ impl<'a> Resolver<'a> {
                     } else if res == Res::Err {
                         return PathResult::NonModule(PartialRes::new(Res::Err));
                     } else if opt_ns.is_some() && (is_last || maybe_assoc) {
-                        self.lint_if_path_starts_with_module(
-                            crate_lint,
-                            path,
-                            path_span,
-                            second_binding,
-                        );
+                        self.lint_if_path_starts_with_module(finalize_full, path, second_binding);
                         return PathResult::NonModule(PartialRes::with_unresolved_segments(
                             res,
                             path.len() - i - 1,
                         ));
                     } else {
-                        let label = format!(
-                            "`{}` is {} {}, not a module",
-                            ident,
-                            res.article(),
-                            res.descr(),
-                        );
-
-                        return PathResult::Failed {
-                            span: ident.span,
-                            label,
-                            suggestion: None,
-                            is_error_from_last_segment: is_last,
-                        };
+                        return PathResult::failed(ident.span, is_last, finalize.is_some(), || {
+                            let label = format!(
+                                "`{ident}` is {} {}, not a module",
+                                res.article(),
+                                res.descr()
+                            );
+                            (label, None)
+                        });
                     }
                 }
                 Err(Undetermined) => return PathResult::Indeterminate,
@@ -2450,196 +2411,192 @@ impl<'a> Resolver<'a> {
                             ));
                         }
                     }
-                    let module_res = match module {
-                        Some(ModuleOrUniformRoot::Module(module)) => module.res(),
-                        _ => None,
-                    };
-                    let (label, suggestion) = if module_res == self.graph_root.res() {
-                        let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _));
-                        // Don't look up import candidates if this is a speculative resolve
-                        let mut candidates = if record_used {
-                            self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod)
-                        } else {
-                            Vec::new()
-                        };
-                        candidates.sort_by_cached_key(|c| {
-                            (c.path.segments.len(), pprust::path_to_string(&c.path))
-                        });
-                        if let Some(candidate) = candidates.get(0) {
-                            (
-                                String::from("unresolved import"),
-                                Some((
-                                    vec![(ident.span, pprust::path_to_string(&candidate.path))],
-                                    String::from("a similar path exists"),
-                                    Applicability::MaybeIncorrect,
-                                )),
-                            )
-                        } else if self.session.edition() == Edition::Edition2015 {
-                            (format!("maybe a missing crate `{}`?", ident), None)
-                        } else {
-                            (format!("could not find `{}` in the crate root", ident), None)
-                        }
-                    } else if i == 0 {
-                        if ident
-                            .name
-                            .as_str()
-                            .chars()
-                            .next()
-                            .map_or(false, |c| c.is_ascii_uppercase())
-                        {
-                            // Check whether the name refers to an item in the value namespace.
-                            let suggestion = if ribs.is_some() {
-                                let match_span = match self.resolve_ident_in_lexical_scope(
-                                    ident,
-                                    ValueNS,
-                                    parent_scope,
-                                    None,
-                                    path_span,
-                                    &ribs.unwrap()[ValueNS],
-                                ) {
-                                    // Name matches a local variable. For example:
-                                    // ```
-                                    // fn f() {
-                                    //     let Foo: &str = "";
-                                    //     println!("{}", Foo::Bar); // Name refers to local
-                                    //                               // variable `Foo`.
-                                    // }
-                                    // ```
-                                    Some(LexicalScopeBinding::Res(Res::Local(id))) => {
-                                        Some(*self.pat_span_map.get(&id).unwrap())
-                                    }
 
-                                    // Name matches item from a local name binding
-                                    // created by `use` declaration. For example:
-                                    // ```
-                                    // pub Foo: &str = "";
-                                    //
-                                    // mod submod {
-                                    //     use super::Foo;
-                                    //     println!("{}", Foo::Bar); // Name refers to local
-                                    //                               // binding `Foo`.
-                                    // }
-                                    // ```
-                                    Some(LexicalScopeBinding::Item(name_binding)) => {
-                                        Some(name_binding.span)
-                                    }
-                                    _ => None,
-                                };
-
-                                if let Some(span) = match_span {
+                    return PathResult::failed(ident.span, is_last, finalize.is_some(), || {
+                        let module_res = match module {
+                            Some(ModuleOrUniformRoot::Module(module)) => module.res(),
+                            _ => None,
+                        };
+                        if module_res == self.graph_root.res() {
+                            let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _));
+                            let mut candidates =
+                                self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod);
+                            candidates.sort_by_cached_key(|c| {
+                                (c.path.segments.len(), pprust::path_to_string(&c.path))
+                            });
+                            if let Some(candidate) = candidates.get(0) {
+                                (
+                                    String::from("unresolved import"),
                                     Some((
-                                        vec![(span, String::from(""))],
-                                        format!("`{}` is defined here, but is not a type", ident),
+                                        vec![(ident.span, pprust::path_to_string(&candidate.path))],
+                                        String::from("a similar path exists"),
                                         Applicability::MaybeIncorrect,
-                                    ))
-                                } else {
-                                    None
-                                }
+                                    )),
+                                )
+                            } else if self.session.edition() == Edition::Edition2015 {
+                                (format!("maybe a missing crate `{}`?", ident), None)
                             } else {
-                                None
-                            };
+                                (format!("could not find `{}` in the crate root", ident), None)
+                            }
+                        } else if i == 0 {
+                            if ident
+                                .name
+                                .as_str()
+                                .chars()
+                                .next()
+                                .map_or(false, |c| c.is_ascii_uppercase())
+                            {
+                                // Check whether the name refers to an item in the value namespace.
+                                let suggestion = if ribs.is_some() {
+                                    let match_span = match self.resolve_ident_in_lexical_scope(
+                                        ident,
+                                        ValueNS,
+                                        parent_scope,
+                                        Finalize::No,
+                                        &ribs.unwrap()[ValueNS],
+                                    ) {
+                                        // Name matches a local variable. For example:
+                                        // ```
+                                        // fn f() {
+                                        //     let Foo: &str = "";
+                                        //     println!("{}", Foo::Bar); // Name refers to local
+                                        //                               // variable `Foo`.
+                                        // }
+                                        // ```
+                                        Some(LexicalScopeBinding::Res(Res::Local(id))) => {
+                                            Some(*self.pat_span_map.get(&id).unwrap())
+                                        }
+
+                                        // Name matches item from a local name binding
+                                        // created by `use` declaration. For example:
+                                        // ```
+                                        // pub Foo: &str = "";
+                                        //
+                                        // mod submod {
+                                        //     use super::Foo;
+                                        //     println!("{}", Foo::Bar); // Name refers to local
+                                        //                               // binding `Foo`.
+                                        // }
+                                        // ```
+                                        Some(LexicalScopeBinding::Item(name_binding)) => {
+                                            Some(name_binding.span)
+                                        }
+                                        _ => None,
+                                    };
 
-                            (format!("use of undeclared type `{}`", ident), suggestion)
-                        } else {
-                            (
-                                format!("use of undeclared crate or module `{}`", ident),
-                                if ident.name == sym::alloc {
-                                    Some((
-                                        vec![],
-                                        String::from(
-                                            "add `extern crate alloc` to use the `alloc` crate",
-                                        ),
-                                        Applicability::MaybeIncorrect,
-                                    ))
+                                    if let Some(span) = match_span {
+                                        Some((
+                                            vec![(span, String::from(""))],
+                                            format!(
+                                                "`{}` is defined here, but is not a type",
+                                                ident
+                                            ),
+                                            Applicability::MaybeIncorrect,
+                                        ))
+                                    } else {
+                                        None
+                                    }
                                 } else {
-                                    self.find_similarly_named_module_or_crate(
-                                        ident.name,
-                                        &parent_scope.module,
-                                    )
-                                    .map(|sugg| {
-                                        (
-                                            vec![(ident.span, sugg.to_string())],
+                                    None
+                                };
+
+                                (format!("use of undeclared type `{}`", ident), suggestion)
+                            } else {
+                                (
+                                    format!("use of undeclared crate or module `{}`", ident),
+                                    if ident.name == sym::alloc {
+                                        Some((
+                                            vec![],
                                             String::from(
-                                                "there is a crate or module with a similar name",
+                                                "add `extern crate alloc` to use the `alloc` crate",
                                             ),
                                             Applicability::MaybeIncorrect,
+                                        ))
+                                    } else {
+                                        self.find_similarly_named_module_or_crate(
+                                            ident.name,
+                                            &parent_scope.module,
                                         )
-                                    })
-                                },
-                            )
-                        }
-                    } else {
-                        let parent = path[i - 1].ident.name;
-                        let parent = match parent {
-                            // ::foo is mounted at the crate root for 2015, and is the extern
-                            // prelude for 2018+
-                            kw::PathRoot if self.session.edition() > Edition::Edition2015 => {
-                                "the list of imported crates".to_owned()
-                            }
-                            kw::PathRoot | kw::Crate => "the crate root".to_owned(),
-                            _ => {
-                                format!("`{}`", parent)
+                                        .map(|sugg| {
+                                            (
+                                                vec![(ident.span, sugg.to_string())],
+                                                String::from(
+                                                    "there is a crate or module with a similar name",
+                                                ),
+                                                Applicability::MaybeIncorrect,
+                                            )
+                                        })
+                                    },
+                                )
                             }
-                        };
-
-                        let mut msg = format!("could not find `{}` in {}", ident, parent);
-                        if ns == TypeNS || ns == ValueNS {
-                            let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS };
-                            if let FindBindingResult::Binding(Ok(binding)) =
-                                find_binding_in_ns(self, ns_to_try)
-                            {
-                                let mut found = |what| {
-                                    msg = format!(
-                                        "expected {}, found {} `{}` in {}",
-                                        ns.descr(),
-                                        what,
-                                        ident,
-                                        parent
-                                    )
-                                };
-                                if binding.module().is_some() {
-                                    found("module")
-                                } else {
-                                    match binding.res() {
-                                        def::Res::<NodeId>::Def(kind, id) => found(kind.descr(id)),
-                                        _ => found(ns_to_try.descr()),
-                                    }
+                        } else {
+                            let parent = path[i - 1].ident.name;
+                            let parent = match parent {
+                                // ::foo is mounted at the crate root for 2015, and is the extern
+                                // prelude for 2018+
+                                kw::PathRoot if self.session.edition() > Edition::Edition2015 => {
+                                    "the list of imported crates".to_owned()
+                                }
+                                kw::PathRoot | kw::Crate => "the crate root".to_owned(),
+                                _ => {
+                                    format!("`{}`", parent)
                                 }
                             };
+
+                            let mut msg = format!("could not find `{}` in {}", ident, parent);
+                            if ns == TypeNS || ns == ValueNS {
+                                let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS };
+                                if let FindBindingResult::Binding(Ok(binding)) =
+                                    find_binding_in_ns(self, ns_to_try)
+                                {
+                                    let mut found = |what| {
+                                        msg = format!(
+                                            "expected {}, found {} `{}` in {}",
+                                            ns.descr(),
+                                            what,
+                                            ident,
+                                            parent
+                                        )
+                                    };
+                                    if binding.module().is_some() {
+                                        found("module")
+                                    } else {
+                                        match binding.res() {
+                                            def::Res::<NodeId>::Def(kind, id) => {
+                                                found(kind.descr(id))
+                                            }
+                                            _ => found(ns_to_try.descr()),
+                                        }
+                                    }
+                                };
+                            }
+                            (msg, None)
                         }
-                        (msg, None)
-                    };
-                    return PathResult::Failed {
-                        span: ident.span,
-                        label,
-                        suggestion,
-                        is_error_from_last_segment: is_last,
-                    };
+                    });
                 }
             }
         }
 
-        self.lint_if_path_starts_with_module(crate_lint, path, path_span, second_binding);
+        self.lint_if_path_starts_with_module(finalize_full, path, second_binding);
 
         PathResult::Module(match module {
             Some(module) => module,
             None if path.is_empty() => ModuleOrUniformRoot::CurrentScope,
-            _ => span_bug!(path_span, "resolve_path: non-empty path `{:?}` has no module", path),
+            _ => bug!("resolve_path: non-empty path `{:?}` has no module", path),
         })
     }
 
     fn lint_if_path_starts_with_module(
         &mut self,
-        crate_lint: CrateLint,
+        finalize: Finalize,
         path: &[Segment],
-        path_span: Span,
         second_binding: Option<&NameBinding<'_>>,
     ) {
-        let (diag_id, diag_span) = match crate_lint {
-            CrateLint::No => return,
-            CrateLint::SimplePath(id) => (id, path_span),
-            CrateLint::UsePath { root_id, root_span } => (root_id, root_span),
-            CrateLint::QPathTrait { qpath_id, qpath_span } => (qpath_id, qpath_span),
+        let (diag_id, diag_span) = match finalize {
+            Finalize::No => return,
+            Finalize::SimplePath(id, path_span) => (id, path_span),
+            Finalize::UsePath { root_id, root_span, .. } => (root_id, root_span),
+            Finalize::QPathTrait { qpath_id, qpath_span, .. } => (qpath_id, qpath_span),
         };
 
         let first_name = match path.get(0) {
@@ -2694,8 +2651,7 @@ impl<'a> Resolver<'a> {
         rib_index: usize,
         rib_ident: Ident,
         mut res: Res,
-        record_used: bool,
-        span: Span,
+        finalize: Option<Span>,
         original_rib_ident_def: Ident,
         all_ribs: &[Rib<'a>],
     ) -> Res {
@@ -2705,7 +2661,7 @@ impl<'a> Resolver<'a> {
 
         // An invalid forward use of a generic parameter from a previous default.
         if let ForwardGenericParamBanRibKind = all_ribs[rib_index].kind {
-            if record_used {
+            if let Some(span) = finalize {
                 let res_error = if rib_ident.name == kw::SelfUpper {
                     ResolutionError::SelfInGenericParamDefault
                 } else {
@@ -2735,17 +2691,17 @@ impl<'a> Resolver<'a> {
                             // This was an attempt to access an upvar inside a
                             // named function item. This is not allowed, so we
                             // report an error.
-                            if record_used {
+                            if let Some(span) = finalize {
                                 // We don't immediately trigger a resolve error, because
                                 // we want certain other resolution errors (namely those
                                 // emitted for `ConstantItemRibKind` below) to take
                                 // precedence.
-                                res_err = Some(CannotCaptureDynamicEnvironmentInFnItem);
+                                res_err = Some((span, CannotCaptureDynamicEnvironmentInFnItem));
                             }
                         }
                         ConstantItemRibKind(_, item) => {
                             // Still doesn't deal with upvars
-                            if record_used {
+                            if let Some(span) = finalize {
                                 let (span, resolution_error) =
                                     if let Some((ident, constant_item_kind)) = item {
                                         let kind_str = match constant_item_kind {
@@ -2773,14 +2729,14 @@ impl<'a> Resolver<'a> {
                             return Res::Err;
                         }
                         ConstParamTyRibKind => {
-                            if record_used {
+                            if let Some(span) = finalize {
                                 self.report_error(span, ParamInTyOfConstParam(rib_ident.name));
                             }
                             return Res::Err;
                         }
                     }
                 }
-                if let Some(res_err) = res_err {
+                if let Some((span, res_err)) = res_err {
                     self.report_error(span, res_err);
                     return Res::Err;
                 }
@@ -2808,7 +2764,7 @@ impl<'a> Resolver<'a> {
                                 if let Res::SelfTy { trait_, alias_to: Some((def, _)) } = res {
                                     res = Res::SelfTy { trait_, alias_to: Some((def, true)) }
                                 } else {
-                                    if record_used {
+                                    if let Some(span) = finalize {
                                         self.report_error(
                                             span,
                                             ResolutionError::ParamInNonTrivialAnonConst {
@@ -2816,9 +2772,9 @@ impl<'a> Resolver<'a> {
                                                 is_type: true,
                                             },
                                         );
+                                        self.session.delay_span_bug(span, CG_BUG_STR);
                                     }
 
-                                    self.session.delay_span_bug(span, CG_BUG_STR);
                                     return Res::Err;
                                 }
                             }
@@ -2830,7 +2786,7 @@ impl<'a> Resolver<'a> {
                         ItemRibKind(has_generic_params) => has_generic_params,
                         FnItemRibKind => HasGenericParams::Yes,
                         ConstParamTyRibKind => {
-                            if record_used {
+                            if let Some(span) = finalize {
                                 self.report_error(
                                     span,
                                     ResolutionError::ParamInTyOfConstParam(rib_ident.name),
@@ -2840,7 +2796,7 @@ impl<'a> Resolver<'a> {
                         }
                     };
 
-                    if record_used {
+                    if let Some(span) = finalize {
                         self.report_error(
                             span,
                             ResolutionError::GenericParamsFromOuterFunction(
@@ -2874,7 +2830,7 @@ impl<'a> Resolver<'a> {
                             let features = self.session.features_untracked();
                             // HACK(min_const_generics): We currently only allow `N` or `{ N }`.
                             if !(trivial || features.generic_const_exprs) {
-                                if record_used {
+                                if let Some(span) = finalize {
                                     self.report_error(
                                         span,
                                         ResolutionError::ParamInNonTrivialAnonConst {
@@ -2882,9 +2838,9 @@ impl<'a> Resolver<'a> {
                                             is_type: false,
                                         },
                                     );
+                                    self.session.delay_span_bug(span, CG_BUG_STR);
                                 }
 
-                                self.session.delay_span_bug(span, CG_BUG_STR);
                                 return Res::Err;
                             }
 
@@ -2894,7 +2850,7 @@ impl<'a> Resolver<'a> {
                         ItemRibKind(has_generic_params) => has_generic_params,
                         FnItemRibKind => HasGenericParams::Yes,
                         ConstParamTyRibKind => {
-                            if record_used {
+                            if let Some(span) = finalize {
                                 self.report_error(
                                     span,
                                     ResolutionError::ParamInTyOfConstParam(rib_ident.name),
@@ -2905,7 +2861,7 @@ impl<'a> Resolver<'a> {
                     };
 
                     // This was an attempt to use a const parameter outside its scope.
-                    if record_used {
+                    if let Some(span) = finalize {
                         self.report_error(
                             span,
                             ResolutionError::GenericParamsFromOuterFunction(
@@ -3293,23 +3249,19 @@ impl<'a> Resolver<'a> {
         err.span_suggestion(span, message, String::new(), Applicability::MachineApplicable);
     }
 
-    fn extern_prelude_get(
-        &mut self,
-        ident: Ident,
-        speculative: bool,
-    ) -> Option<&'a NameBinding<'a>> {
+    fn extern_prelude_get(&mut self, ident: Ident, finalize: bool) -> Option<&'a NameBinding<'a>> {
         if ident.is_path_segment_keyword() {
             // Make sure `self`, `super` etc produce an error when passed to here.
             return None;
         }
         self.extern_prelude.get(&ident.normalize_to_macros_2_0()).cloned().and_then(|entry| {
             if let Some(binding) = entry.extern_crate_item {
-                if !speculative && entry.introduced_by_item {
+                if finalize && entry.introduced_by_item {
                     self.record_use(ident, binding, false);
                 }
                 Some(binding)
             } else {
-                let crate_id = if !speculative {
+                let crate_id = if finalize {
                     let Some(crate_id) =
                         self.crate_loader.process_path_extern(ident.name, ident.span) else { return Some(self.dummy_binding); };
                     crate_id
@@ -3325,83 +3277,38 @@ impl<'a> Resolver<'a> {
         })
     }
 
-    /// Rustdoc uses this to resolve things in a recoverable way. `ResolutionError<'a>`
+    /// Rustdoc uses this to resolve doc link paths in a recoverable way. `PathResult<'a>`
     /// isn't something that can be returned because it can't be made to live that long,
     /// and also it's a private type. Fortunately rustdoc doesn't need to know the error,
     /// just that an error occurred.
-    // FIXME(Manishearth): intra-doc links won't get warned of epoch changes.
-    pub fn resolve_str_path_error(
+    pub fn resolve_rustdoc_path(
         &mut self,
-        span: Span,
         path_str: &str,
         ns: Namespace,
         module_id: DefId,
-    ) -> Result<(ast::Path, Res), ()> {
-        let path = if path_str.starts_with("::") {
-            ast::Path {
-                span,
-                segments: iter::once(Ident::with_dummy_span(kw::PathRoot))
-                    .chain(path_str.split("::").skip(1).map(Ident::from_str))
-                    .map(|i| self.new_ast_path_segment(i))
-                    .collect(),
-                tokens: None,
-            }
-        } else {
-            ast::Path {
-                span,
-                segments: path_str
-                    .split("::")
-                    .map(Ident::from_str)
-                    .map(|i| self.new_ast_path_segment(i))
-                    .collect(),
-                tokens: None,
-            }
-        };
-        let module = self.expect_module(module_id);
-        let parent_scope = &ParentScope::module(module, self);
-        let res = self.resolve_ast_path(&path, ns, parent_scope).map_err(|_| ())?;
-        Ok((path, res))
-    }
+    ) -> Option<Res> {
+        let mut segments =
+            Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident));
+        if path_str.starts_with("::") {
+            segments[0].ident.name = kw::PathRoot;
+        }
 
-    // Resolve a path passed from rustdoc or HIR lowering.
-    fn resolve_ast_path(
-        &mut self,
-        path: &ast::Path,
-        ns: Namespace,
-        parent_scope: &ParentScope<'a>,
-    ) -> Result<Res, (Span, ResolutionError<'a>)> {
+        let module = self.expect_module(module_id);
         match self.resolve_path(
-            &Segment::from_path(path),
+            &segments,
             Some(ns),
-            parent_scope,
-            false,
-            path.span,
-            CrateLint::No,
+            &ParentScope::module(module, self),
+            Finalize::No,
         ) {
-            PathResult::Module(ModuleOrUniformRoot::Module(module)) => Ok(module.res().unwrap()),
+            PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()),
             PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
-                Ok(path_res.base_res())
+                Some(path_res.base_res())
             }
-            PathResult::NonModule(..) => Err((
-                path.span,
-                ResolutionError::FailedToResolve {
-                    label: String::from("type-relative paths are not supported in this context"),
-                    suggestion: None,
-                },
-            )),
+            PathResult::NonModule(..) | PathResult::Failed { .. } => None,
             PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
-            PathResult::Failed { span, label, suggestion, .. } => {
-                Err((span, ResolutionError::FailedToResolve { label, suggestion }))
-            }
         }
     }
 
-    fn new_ast_path_segment(&mut self, ident: Ident) -> ast::PathSegment {
-        let mut seg = ast::PathSegment::from_ident(ident);
-        seg.id = self.next_node_id();
-        seg
-    }
-
     // For rustdoc.
     pub fn graph_root(&self) -> Module<'a> {
         self.graph_root
@@ -3485,8 +3392,7 @@ impl<'a> Resolver<'a> {
             ident,
             ValueNS,
             parent_scope,
-            false,
-            DUMMY_SP,
+            None
         ) else {
             return;
         };
@@ -3544,35 +3450,43 @@ fn module_to_string(module: Module<'_>) -> Option<String> {
 }
 
 #[derive(Copy, Clone, Debug)]
-enum CrateLint {
+enum Finalize {
     /// Do not issue the lint.
     No,
 
     /// This lint applies to some arbitrary path; e.g., `impl ::foo::Bar`.
     /// In this case, we can take the span of that path.
-    SimplePath(NodeId),
+    SimplePath(NodeId, Span),
 
     /// This lint comes from a `use` statement. In this case, what we
     /// care about really is the *root* `use` statement; e.g., if we
     /// have nested things like `use a::{b, c}`, we care about the
     /// `use a` part.
-    UsePath { root_id: NodeId, root_span: Span },
+    UsePath { root_id: NodeId, root_span: Span, path_span: Span },
 
     /// This is the "trait item" from a fully qualified path. For example,
     /// we might be resolving  `X::Y::Z` from a path like `<T as X::Y>::Z`.
     /// The `path_span` is the span of the to the trait itself (`X::Y`).
-    QPathTrait { qpath_id: NodeId, qpath_span: Span },
+    QPathTrait { qpath_id: NodeId, qpath_span: Span, path_span: Span },
 }
 
-impl CrateLint {
-    fn node_id(&self) -> Option<NodeId> {
+impl Finalize {
+    fn node_id_and_path_span(&self) -> Option<(NodeId, Span)> {
         match *self {
-            CrateLint::No => None,
-            CrateLint::SimplePath(id)
-            | CrateLint::UsePath { root_id: id, .. }
-            | CrateLint::QPathTrait { qpath_id: id, .. } => Some(id),
+            Finalize::No => None,
+            Finalize::SimplePath(id, path_span)
+            | Finalize::UsePath { root_id: id, path_span, .. }
+            | Finalize::QPathTrait { qpath_id: id, path_span, .. } => Some((id, path_span)),
         }
     }
+
+    fn node_id(&self) -> Option<NodeId> {
+        self.node_id_and_path_span().map(|(id, _)| id)
+    }
+
+    fn path_span(&self) -> Option<Span> {
+        self.node_id_and_path_span().map(|(_, path_span)| path_span)
+    }
 }
 
 pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index e34d3e605ec..dc94ba49a54 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -4,7 +4,7 @@
 use crate::imports::ImportResolver;
 use crate::Namespace::*;
 use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BuiltinMacroState, Determinacy};
-use crate::{CrateLint, DeriveData, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak};
+use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak};
 use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
 use rustc_ast::{self as ast, Inline, ItemKind, ModKind, NodeId};
 use rustc_ast_lowering::ResolverAstLowering;
@@ -415,7 +415,7 @@ impl<'a> ResolverExpand for Resolver<'a> {
 
         let mut indeterminate = false;
         for ns in [TypeNS, ValueNS, MacroNS].iter().copied() {
-            match self.resolve_path(path, Some(ns), &parent_scope, false, span, CrateLint::No) {
+            match self.resolve_path(path, Some(ns), &parent_scope, Finalize::No) {
                 PathResult::Module(ModuleOrUniformRoot::Module(_)) => return Ok(true),
                 PathResult::NonModule(partial_res) if partial_res.unresolved_segments() == 0 => {
                     return Ok(true);
@@ -575,14 +575,7 @@ impl<'a> Resolver<'a> {
         }
 
         let res = if path.len() > 1 {
-            let res = match self.resolve_path(
-                &path,
-                Some(MacroNS),
-                parent_scope,
-                false,
-                path_span,
-                CrateLint::No,
-            ) {
+            let res = match self.resolve_path(&path, Some(MacroNS), parent_scope, Finalize::No) {
                 PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
                     Ok(path_res.base_res())
                 }
@@ -612,9 +605,8 @@ impl<'a> Resolver<'a> {
                 path[0].ident,
                 scope_set,
                 parent_scope,
-                false,
+                None,
                 force,
-                path_span,
             );
             if let Err(Determinacy::Undetermined) = binding {
                 return Err(Determinacy::Undetermined);
@@ -648,9 +640,8 @@ impl<'a> Resolver<'a> {
         orig_ident: Ident,
         scope_set: ScopeSet<'a>,
         parent_scope: &ParentScope<'a>,
-        record_used: bool,
+        finalize: Option<Span>,
         force: bool,
-        path_span: Span,
     ) -> Result<&'a NameBinding<'a>, Determinacy> {
         bitflags::bitflags! {
             struct Flags: u8 {
@@ -662,7 +653,7 @@ impl<'a> Resolver<'a> {
             }
         }
 
-        assert!(force || !record_used); // `record_used` implies `force`
+        assert!(force || !finalize.is_some()); // `finalize` implies `force`
 
         // Make sure `self`, `super` etc produce an error when passed to here.
         if orig_ident.is_path_segment_keyword() {
@@ -769,8 +760,7 @@ impl<'a> Resolver<'a> {
                             ident,
                             ns,
                             parent_scope,
-                            record_used,
-                            path_span,
+                            finalize,
                         );
                         match binding {
                             Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)),
@@ -791,8 +781,7 @@ impl<'a> Resolver<'a> {
                             ns,
                             adjusted_parent_scope,
                             !matches!(scope_set, ScopeSet::Late(..)),
-                            record_used,
-                            path_span,
+                            finalize,
                         );
                         match binding {
                             Ok(binding) => {
@@ -856,12 +845,14 @@ impl<'a> Resolver<'a> {
                             Err(Determinacy::Determined)
                         }
                     }
-                    Scope::ExternPrelude => match this.extern_prelude_get(ident, !record_used) {
-                        Some(binding) => Ok((binding, Flags::empty())),
-                        None => Err(Determinacy::determined(
-                            this.graph_root.unexpanded_invocations.borrow().is_empty(),
-                        )),
-                    },
+                    Scope::ExternPrelude => {
+                        match this.extern_prelude_get(ident, finalize.is_some()) {
+                            Some(binding) => Ok((binding, Flags::empty())),
+                            None => Err(Determinacy::determined(
+                                this.graph_root.unexpanded_invocations.borrow().is_empty(),
+                            )),
+                        }
+                    }
                     Scope::ToolPrelude => match this.registered_tools.get(&ident).cloned() {
                         Some(ident) => ok(Res::ToolMod, ident.span, this.arenas),
                         None => Err(Determinacy::Determined),
@@ -874,8 +865,7 @@ impl<'a> Resolver<'a> {
                                 ident,
                                 ns,
                                 parent_scope,
-                                false,
-                                path_span,
+                                None,
                             ) {
                                 if use_prelude || this.is_builtin_macro(binding.res()) {
                                     result = Ok((binding, Flags::MISC_FROM_PRELUDE));
@@ -894,7 +884,7 @@ impl<'a> Resolver<'a> {
                     Ok((binding, flags))
                         if sub_namespace_match(binding.macro_kind(), macro_kind) =>
                     {
-                        if !record_used || matches!(scope_set, ScopeSet::Late(..)) {
+                        if finalize.is_none() || matches!(scope_set, ScopeSet::Late(..)) {
                             return Some(Ok(binding));
                         }
 
@@ -1033,9 +1023,7 @@ impl<'a> Resolver<'a> {
                 &path,
                 Some(MacroNS),
                 &parent_scope,
-                true,
-                path_span,
-                CrateLint::No,
+                Finalize::SimplePath(ast::CRATE_NODE_ID, path_span),
             ) {
                 PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
                     let res = path_res.base_res();
@@ -1069,9 +1057,8 @@ impl<'a> Resolver<'a> {
                 ident,
                 ScopeSet::Macro(kind),
                 &parent_scope,
+                Some(ident.span),
                 true,
-                true,
-                ident.span,
             ) {
                 Ok(binding) => {
                     let initial_res = initial_binding.map(|initial_binding| {
@@ -1111,9 +1098,8 @@ impl<'a> Resolver<'a> {
                 ident,
                 ScopeSet::Macro(MacroKind::Attr),
                 &parent_scope,
+                Some(ident.span),
                 true,
-                true,
-                ident.span,
             );
         }
     }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 48ca321b737..5cf362bfa7e 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1347,6 +1347,10 @@ symbols! {
         store,
         str,
         str_alloc,
+        str_split_whitespace,
+        str_trim,
+        str_trim_end,
+        str_trim_start,
         stringify,
         stringify_macro,
         struct_field_attributes,
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 94a4001bbb9..ec4f8084e21 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -7,7 +7,7 @@
 use crate::infer::outlives::env::OutlivesEnvironment;
 use crate::infer::{CombinedSnapshot, InferOk, RegionckMode};
 use crate::traits::select::IntercrateAmbiguityCause;
-use crate::traits::util::impl_trait_ref_and_oblig;
+use crate::traits::util::impl_subject_and_oblig;
 use crate::traits::SkipLeakCheck;
 use crate::traits::{
     self, FulfillmentContext, Normalized, Obligation, ObligationCause, PredicateObligation,
@@ -23,9 +23,10 @@ use rustc_middle::traits::specialization_graph::OverlapMode;
 use rustc_middle::ty::fast_reject::{self, TreatParams};
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::subst::Subst;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt};
 use rustc_span::symbol::sym;
 use rustc_span::DUMMY_SP;
+use std::fmt::Debug;
 use std::iter;
 
 /// Whether we do the orphan check relative to this crate or
@@ -300,60 +301,62 @@ fn negative_impl<'cx, 'tcx>(
     debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
     let tcx = selcx.infcx().tcx;
 
-    // create a parameter environment corresponding to a (placeholder) instantiation of impl1
-    let impl1_env = tcx.param_env(impl1_def_id);
-    let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();
-
     // Create an infcx, taking the predicates of impl1 as assumptions:
     tcx.infer_ctxt().enter(|infcx| {
-        // Normalize the trait reference. The WF rules ought to ensure
-        // that this always succeeds.
-        let impl1_trait_ref = match traits::fully_normalize(
+        // create a parameter environment corresponding to a (placeholder) instantiation of impl1
+        let impl_env = tcx.param_env(impl1_def_id);
+        let subject1 = match traits::fully_normalize(
             &infcx,
             FulfillmentContext::new(),
             ObligationCause::dummy(),
-            impl1_env,
-            impl1_trait_ref,
+            impl_env,
+            tcx.impl_subject(impl1_def_id),
         ) {
-            Ok(impl1_trait_ref) => impl1_trait_ref,
-            Err(err) => {
-                bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err);
-            }
+            Ok(s) => s,
+            Err(err) => bug!("failed to fully normalize {:?}: {:?}", impl1_def_id, err),
         };
 
         // Attempt to prove that impl2 applies, given all of the above.
         let selcx = &mut SelectionContext::new(&infcx);
         let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
-        let (impl2_trait_ref, obligations) =
-            impl_trait_ref_and_oblig(selcx, impl1_env, impl2_def_id, impl2_substs);
-
-        // do the impls unify? If not, not disjoint.
-        let Ok(InferOk { obligations: more_obligations, .. }) = infcx
-            .at(&ObligationCause::dummy(), impl1_env)
-            .eq(impl1_trait_ref, impl2_trait_ref)
-        else {
-            debug!(
-                "explicit_disjoint: {:?} does not unify with {:?}",
-                impl1_trait_ref, impl2_trait_ref
-            );
-            return false;
-        };
+        let (subject2, obligations) =
+            impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs);
 
-        let opt_failing_obligation = obligations
-            .into_iter()
-            .chain(more_obligations)
-            .find(|o| negative_impl_exists(selcx, impl1_env, impl1_def_id, o));
-
-        if let Some(failing_obligation) = opt_failing_obligation {
-            debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
-            true
-        } else {
-            false
-        }
+        !equate(&infcx, impl_env, impl1_def_id, subject1, subject2, obligations)
     })
 }
 
-/// Try to prove that a negative impl exist for the given obligation and their super predicates.
+fn equate<'cx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'tcx>,
+    impl_env: ty::ParamEnv<'tcx>,
+    impl1_def_id: DefId,
+    subject1: ImplSubject<'tcx>,
+    subject2: ImplSubject<'tcx>,
+    obligations: impl Iterator<Item = PredicateObligation<'tcx>>,
+) -> bool {
+    // do the impls unify? If not, not disjoint.
+    let Ok(InferOk { obligations: more_obligations, .. }) =
+        infcx.at(&ObligationCause::dummy(), impl_env).eq(subject1, subject2)
+    else {
+        debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2);
+        return true;
+    };
+
+    let selcx = &mut SelectionContext::new(&infcx);
+    let opt_failing_obligation = obligations
+        .into_iter()
+        .chain(more_obligations)
+        .find(|o| negative_impl_exists(selcx, impl_env, impl1_def_id, o));
+
+    if let Some(failing_obligation) = opt_failing_obligation {
+        debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
+        false
+    } else {
+        true
+    }
+}
+
+/// Try to prove that a negative impl exist for the given obligation and its super predicates.
 #[instrument(level = "debug", skip(selcx))]
 fn negative_impl_exists<'cx, 'tcx>(
     selcx: &SelectionContext<'cx, 'tcx>,
@@ -367,7 +370,7 @@ fn negative_impl_exists<'cx, 'tcx>(
         return true;
     }
 
-    // Try to prove a negative obligation exist for super predicates
+    // Try to prove a negative obligation exists for super predicates
     for o in util::elaborate_predicates(infcx.tcx, iter::once(o.predicate)) {
         if resolve_negative_obligation(infcx, param_env, region_context, &o) {
             return true;
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 490c04e7be2..959b644becd 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -188,6 +188,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
     }
 }
 
+#[instrument(skip(tcx), level = "debug")]
 fn satisfied_from_param_env<'tcx>(
     tcx: TyCtxt<'tcx>,
     ct: AbstractConst<'tcx>,
@@ -197,14 +198,17 @@ fn satisfied_from_param_env<'tcx>(
         match pred.kind().skip_binder() {
             ty::PredicateKind::ConstEvaluatable(uv) => {
                 if let Some(b_ct) = AbstractConst::new(tcx, uv)? {
+                    let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
+
                     // Try to unify with each subtree in the AbstractConst to allow for
                     // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
                     // predicate for `(N + 1) * 2`
-                    let result =
-                        walk_abstract_const(tcx, b_ct, |b_ct| match try_unify(tcx, ct, b_ct) {
+                    let result = walk_abstract_const(tcx, b_ct, |b_ct| {
+                        match const_unify_ctxt.try_unify(ct, b_ct) {
                             true => ControlFlow::BREAK,
                             false => ControlFlow::CONTINUE,
-                        });
+                        }
+                    });
 
                     if let ControlFlow::Break(()) = result {
                         debug!("is_const_evaluatable: abstract_const ~~> ok");
@@ -637,11 +641,13 @@ pub(super) fn thir_abstract_const<'tcx>(
 pub(super) fn try_unify_abstract_consts<'tcx>(
     tcx: TyCtxt<'tcx>,
     (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>),
+    param_env: ty::ParamEnv<'tcx>,
 ) -> bool {
     (|| {
         if let Some(a) = AbstractConst::new(tcx, a)? {
             if let Some(b) = AbstractConst::new(tcx, b)? {
-                return Ok(try_unify(tcx, a, b));
+                let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
+                return Ok(const_unify_ctxt.try_unify(a, b));
             }
         }
 
@@ -689,88 +695,115 @@ where
     recurse(tcx, ct, &mut f)
 }
 
-/// Tries to unify two abstract constants using structural equality.
-pub(super) fn try_unify<'tcx>(
+struct ConstUnifyCtxt<'tcx> {
     tcx: TyCtxt<'tcx>,
-    mut a: AbstractConst<'tcx>,
-    mut b: AbstractConst<'tcx>,
-) -> bool {
-    // We substitute generics repeatedly to allow AbstractConsts to unify where a
+    param_env: ty::ParamEnv<'tcx>,
+}
+
+impl<'tcx> ConstUnifyCtxt<'tcx> {
+    // Substitutes generics repeatedly to allow AbstractConsts to unify where a
     // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g.
     // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])]
-    while let Node::Leaf(a_ct) = a.root(tcx) {
-        match AbstractConst::from_const(tcx, a_ct) {
-            Ok(Some(a_act)) => a = a_act,
-            Ok(None) => break,
-            Err(_) => return true,
-        }
-    }
-    while let Node::Leaf(b_ct) = b.root(tcx) {
-        match AbstractConst::from_const(tcx, b_ct) {
-            Ok(Some(b_act)) => b = b_act,
-            Ok(None) => break,
-            Err(_) => return true,
+    #[inline]
+    #[instrument(skip(self), level = "debug")]
+    fn try_replace_substs_in_root(
+        &self,
+        mut abstr_const: AbstractConst<'tcx>,
+    ) -> Option<AbstractConst<'tcx>> {
+        while let Node::Leaf(ct) = abstr_const.root(self.tcx) {
+            match AbstractConst::from_const(self.tcx, ct) {
+                Ok(Some(act)) => abstr_const = act,
+                Ok(None) => break,
+                Err(_) => return None,
+            }
         }
-    }
 
-    match (a.root(tcx), b.root(tcx)) {
-        (Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
-            if a_ct.ty() != b_ct.ty() {
-                return false;
-            }
+        Some(abstr_const)
+    }
 
-            match (a_ct.val(), b_ct.val()) {
-                // We can just unify errors with everything to reduce the amount of
-                // emitted errors here.
-                (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true,
-                (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => {
-                    a_param == b_param
+    /// Tries to unify two abstract constants using structural equality.
+    #[instrument(skip(self), level = "debug")]
+    fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool {
+        let a = if let Some(a) = self.try_replace_substs_in_root(a) {
+            a
+        } else {
+            return true;
+        };
+
+        let b = if let Some(b) = self.try_replace_substs_in_root(b) {
+            b
+        } else {
+            return true;
+        };
+
+        let a_root = a.root(self.tcx);
+        let b_root = b.root(self.tcx);
+        debug!(?a_root, ?b_root);
+
+        match (a_root, b_root) {
+            (Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
+                let a_ct = a_ct.eval(self.tcx, self.param_env);
+                debug!("a_ct evaluated: {:?}", a_ct);
+                let b_ct = b_ct.eval(self.tcx, self.param_env);
+                debug!("b_ct evaluated: {:?}", b_ct);
+
+                if a_ct.ty() != b_ct.ty() {
+                    return false;
                 }
-                (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
-                // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]`
-                // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This
-                // means that we only allow inference variables if they are equal.
-                (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val,
-                // We expand generic anonymous constants at the start of this function, so this
-                // branch should only be taking when dealing with associated constants, at
-                // which point directly comparing them seems like the desired behavior.
-                //
-                // FIXME(generic_const_exprs): This isn't actually the case.
-                // We also take this branch for concrete anonymous constants and
-                // expand generic anonymous constants with concrete substs.
-                (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => {
-                    a_uv == b_uv
+
+                match (a_ct.val(), b_ct.val()) {
+                    // We can just unify errors with everything to reduce the amount of
+                    // emitted errors here.
+                    (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true,
+                    (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => {
+                        a_param == b_param
+                    }
+                    (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
+                    // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]`
+                    // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This
+                    // means that we only allow inference variables if they are equal.
+                    (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val,
+                    // We expand generic anonymous constants at the start of this function, so this
+                    // branch should only be taking when dealing with associated constants, at
+                    // which point directly comparing them seems like the desired behavior.
+                    //
+                    // FIXME(generic_const_exprs): This isn't actually the case.
+                    // We also take this branch for concrete anonymous constants and
+                    // expand generic anonymous constants with concrete substs.
+                    (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => {
+                        a_uv == b_uv
+                    }
+                    // FIXME(generic_const_exprs): We may want to either actually try
+                    // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like
+                    // this, for now we just return false here.
+                    _ => false,
                 }
-                // FIXME(generic_const_exprs): We may want to either actually try
-                // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like
-                // this, for now we just return false here.
-                _ => false,
             }
+            (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
+                self.try_unify(a.subtree(al), b.subtree(bl))
+                    && self.try_unify(a.subtree(ar), b.subtree(br))
+            }
+            (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
+                self.try_unify(a.subtree(av), b.subtree(bv))
+            }
+            (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
+                if a_args.len() == b_args.len() =>
+            {
+                self.try_unify(a.subtree(a_f), b.subtree(b_f))
+                    && iter::zip(a_args, b_args)
+                        .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn)))
+            }
+            (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty))
+                if (a_ty == b_ty) && (a_kind == b_kind) =>
+            {
+                self.try_unify(a.subtree(a_operand), b.subtree(b_operand))
+            }
+            // use this over `_ => false` to make adding variants to `Node` less error prone
+            (Node::Cast(..), _)
+            | (Node::FunctionCall(..), _)
+            | (Node::UnaryOp(..), _)
+            | (Node::Binop(..), _)
+            | (Node::Leaf(..), _) => false,
         }
-        (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
-            try_unify(tcx, a.subtree(al), b.subtree(bl))
-                && try_unify(tcx, a.subtree(ar), b.subtree(br))
-        }
-        (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
-            try_unify(tcx, a.subtree(av), b.subtree(bv))
-        }
-        (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
-            if a_args.len() == b_args.len() =>
-        {
-            try_unify(tcx, a.subtree(a_f), b.subtree(b_f))
-                && iter::zip(a_args, b_args)
-                    .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn)))
-        }
-        (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty))
-            if (a_ty == b_ty) && (a_kind == b_kind) =>
-        {
-            try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand))
-        }
-        // use this over `_ => false` to make adding variants to `Node` less error prone
-        (Node::Cast(..), _)
-        | (Node::FunctionCall(..), _)
-        | (Node::UnaryOp(..), _)
-        | (Node::Binop(..), _)
-        | (Node::Leaf(..), _) => false,
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 229e108d5d6..5e220173cae 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -22,6 +22,7 @@ use rustc_hir::GenericParam;
 use rustc_hir::Item;
 use rustc_hir::Node;
 use rustc_middle::thir::abstract_const::NotConstEvaluatable;
+use rustc_middle::traits::select::OverflowError;
 use rustc_middle::ty::error::ExpectedFound;
 use rustc_middle::ty::fold::TypeFolder;
 use rustc_middle::ty::{
@@ -928,8 +929,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error");
                 return;
             }
-
-            Overflow => {
+            // Already reported.
+            Overflow(OverflowError::Error(_)) => {
+                self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported");
+                return;
+            }
+            Overflow(_) => {
                 bug!("overflow should be handled before the `report_selection_error` path");
             }
             SelectionError::ErrorReporting => {
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 9ac8dc59a1d..1b862834467 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -580,7 +580,11 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
                         if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
                             (c1.val(), c2.val())
                         {
-                            if infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) {
+                            if infcx.try_unify_abstract_consts(
+                                a.shrink(),
+                                b.shrink(),
+                                obligation.param_env,
+                            ) {
                                 return ProcessResult::Changed(vec![]);
                             }
                         }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 3a6ca9b7624..88750f272c8 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -862,7 +862,10 @@ pub fn provide(providers: &mut ty::query::Providers) {
                 ty::WithOptConstParam { did, const_param_did: Some(param_did) },
             )
         },
-        try_unify_abstract_consts: const_evaluatable::try_unify_abstract_consts,
+        try_unify_abstract_consts: |tcx, param_env_and| {
+            let (param_env, (a, b)) = param_env_and.into_parts();
+            const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env)
+        },
         ..*providers
     };
 }
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 390381752f9..b61e6873571 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -27,6 +27,7 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
+use rustc_middle::traits::select::OverflowError;
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
 use rustc_middle::ty::subst::Subst;
 use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt};
@@ -1139,7 +1140,9 @@ fn project<'cx, 'tcx>(
     if !selcx.tcx().recursion_limit().value_within_limit(obligation.recursion_depth) {
         // This should really be an immediate error, but some existing code
         // relies on being able to recover from this.
-        return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow));
+        return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow(
+            OverflowError::Canonical,
+        )));
     }
 
     if obligation.predicate.references_error() {
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index 4874ba6f58c..db45ee3fed7 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -108,9 +108,11 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
                         )
                     }
                     OverflowError::ErrorReporting => EvaluationResult::EvaluatedToErr,
+                    OverflowError::Error(_) => EvaluationResult::EvaluatedToErr,
                 })
             }
             Err(OverflowError::ErrorReporting) => EvaluationResult::EvaluatedToErr,
+            Err(OverflowError::Error(_)) => EvaluationResult::EvaluatedToErr,
         }
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index c0a283d2eda..3e7a2252318 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -164,8 +164,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
                 }
                 Ok(_) => Ok(None),
-                Err(OverflowError::Canonical) => Err(Overflow),
+                Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)),
                 Err(OverflowError::ErrorReporting) => Err(ErrorReporting),
+                Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))),
             })
             .flat_map(Result::transpose)
             .collect::<Result<Vec<_>, _>>()?;
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 3a5bca49296..6d232d86d8a 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -25,7 +25,7 @@ use crate::traits::project::ProjectionCacheKeyExt;
 use crate::traits::ProjectionCacheKey;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::Diagnostic;
+use rustc_errors::{Diagnostic, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::LateBoundRegionConversionTime;
@@ -316,11 +316,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         obligation: &TraitObligation<'tcx>,
     ) -> SelectionResult<'tcx, Selection<'tcx>> {
         let candidate = match self.select_from_obligation(obligation) {
-            Err(SelectionError::Overflow) => {
+            Err(SelectionError::Overflow(OverflowError::Canonical)) => {
                 // In standard mode, overflow must have been caught and reported
                 // earlier.
                 assert!(self.query_mode == TraitQueryMode::Canonical);
-                return Err(SelectionError::Overflow);
+                return Err(SelectionError::Overflow(OverflowError::Canonical));
             }
             Err(SelectionError::Ambiguous(_)) => {
                 return Ok(None);
@@ -335,9 +335,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         };
 
         match self.confirm_candidate(obligation, candidate) {
-            Err(SelectionError::Overflow) => {
+            Err(SelectionError::Overflow(OverflowError::Canonical)) => {
                 assert!(self.query_mode == TraitQueryMode::Canonical);
-                Err(SelectionError::Overflow)
+                Err(SelectionError::Overflow(OverflowError::Canonical))
             }
             Err(e) => Err(e),
             Ok(candidate) => {
@@ -639,7 +639,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
                             (c1.val(), c2.val())
                         {
-                            if self.infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) {
+                            if self.infcx.try_unify_abstract_consts(
+                                a.shrink(),
+                                b.shrink(),
+                                obligation.param_env,
+                            ) {
                                 return Ok(EvaluatedToOk);
                             }
                         }
@@ -954,7 +958,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             Ok(Some(c)) => self.evaluate_candidate(stack, &c),
             Err(SelectionError::Ambiguous(_)) => Ok(EvaluatedToAmbig),
             Ok(None) => Ok(EvaluatedToAmbig),
-            Err(Overflow) => Err(OverflowError::Canonical),
+            Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical),
             Err(ErrorReporting) => Err(OverflowError::ErrorReporting),
             Err(..) => Ok(EvaluatedToErr),
         }
@@ -1113,7 +1117,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             match self.query_mode {
                 TraitQueryMode::Standard => {
                     if self.infcx.is_tainted_by_errors() {
-                        return Err(OverflowError::ErrorReporting);
+                        return Err(OverflowError::Error(
+                            ErrorGuaranteed::unchecked_claim_error_was_emitted(),
+                        ));
                     }
                     self.infcx.report_overflow_error(error_obligation, true);
                 }
@@ -1349,7 +1355,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         if self.can_use_global_caches(param_env) {
-            if let Err(Overflow) = candidate {
+            if let Err(Overflow(OverflowError::Canonical)) = candidate {
                 // Don't cache overflow globally; we only produce this in certain modes.
             } else if !pred.needs_infer() {
                 if !candidate.needs_infer() {
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 79471065ccc..328e0d2e0e9 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -20,12 +20,12 @@ use rustc_errors::{struct_span_err, EmissionGuarantee};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::lint::LintDiagnosticBuilder;
 use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, ImplSubject, TyCtxt};
 use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
 use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
 use rustc_span::{Span, DUMMY_SP};
 
-use super::util::impl_trait_ref_and_oblig;
+use super::util;
 use super::{FulfillmentContext, SelectionContext};
 
 /// Information pertinent to an overlapping impl error.
@@ -186,18 +186,20 @@ fn fulfill_implication<'a, 'tcx>(
         param_env, source_trait_ref, target_impl
     );
 
+    let source_trait = ImplSubject::Trait(source_trait_ref);
+
     let selcx = &mut SelectionContext::new(&infcx);
     let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl);
-    let (target_trait_ref, obligations) =
-        impl_trait_ref_and_oblig(selcx, param_env, target_impl, target_substs);
+    let (target_trait, obligations) =
+        util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs);
 
     // do the impls unify? If not, no specialization.
     let Ok(InferOk { obligations: more_obligations, .. }) =
-        infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref)
+        infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait, target_trait)
     else {
         debug!(
             "fulfill_implication: {:?} does not unify with {:?}",
-            source_trait_ref, target_trait_ref
+            source_trait, target_trait
         );
         return Err(());
     };
@@ -225,7 +227,7 @@ fn fulfill_implication<'a, 'tcx>(
             [] => {
                 debug!(
                     "fulfill_implication: an impl for {:?} specializes {:?}",
-                    source_trait_ref, target_trait_ref
+                    source_trait, target_trait
                 );
 
                 // Now resolve the *substitution* we built for the target earlier, replacing
@@ -237,8 +239,8 @@ fn fulfill_implication<'a, 'tcx>(
                 debug!(
                     "fulfill_implication: for impls on {:?} and {:?}, \
                      could not fulfill: {:?} given {:?}",
-                    source_trait_ref,
-                    target_trait_ref,
+                    source_trait,
+                    target_trait,
                     errors,
                     param_env.caller_bounds()
                 );
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index f800e7a1402..7543d1f9a7b 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -6,7 +6,7 @@ use smallvec::SmallVec;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef};
-use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeFoldable};
 
 use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext};
 pub use rustc_infer::traits::{self, util::*};
@@ -190,19 +190,19 @@ impl Iterator for SupertraitDefIds<'_> {
 // Other
 ///////////////////////////////////////////////////////////////////////////
 
-/// Instantiate all bound parameters of the impl with the given substs,
-/// returning the resulting trait ref and all obligations that arise.
+/// Instantiate all bound parameters of the impl subject with the given substs,
+/// returning the resulting subject and all obligations that arise.
 /// The obligations are closed under normalization.
-pub fn impl_trait_ref_and_oblig<'a, 'tcx>(
+pub fn impl_subject_and_oblig<'a, 'tcx>(
     selcx: &mut SelectionContext<'a, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     impl_def_id: DefId,
     impl_substs: SubstsRef<'tcx>,
-) -> (ty::TraitRef<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
-    let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap();
-    let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs);
-    let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } =
-        super::normalize(selcx, param_env, ObligationCause::dummy(), impl_trait_ref);
+) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
+    let subject = selcx.tcx().impl_subject(impl_def_id);
+    let subject = subject.subst(selcx.tcx(), impl_substs);
+    let Normalized { value: subject, obligations: normalization_obligations1 } =
+        super::normalize(selcx, param_env, ObligationCause::dummy(), subject);
 
     let predicates = selcx.tcx().predicates_of(impl_def_id);
     let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
@@ -215,7 +215,7 @@ pub fn impl_trait_ref_and_oblig<'a, 'tcx>(
         .chain(normalization_obligations1.into_iter())
         .chain(normalization_obligations2.into_iter());
 
-    (impl_trait_ref, impl_obligations)
+    (subject, impl_obligations)
 }
 
 pub fn predicates_for_generics<'tcx>(
diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs
index d38777bea59..4ab94f39357 100644
--- a/compiler/rustc_typeck/src/check/dropck.rs
+++ b/compiler/rustc_typeck/src/check/dropck.rs
@@ -243,7 +243,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
                 (
                     ty::PredicateKind::ConstEvaluatable(a),
                     ty::PredicateKind::ConstEvaluatable(b),
-                ) => tcx.try_unify_abstract_consts((a, b)),
+                ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))),
                 (
                     ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)),
                     ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)),
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index baa23e08fe7..4751faeb936 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -2226,6 +2226,7 @@ macro_rules! uint_impl {
         #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_next_power_of_two(), 4);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_next_power_of_two(), 0);")]
         /// ```
+        #[inline]
         #[unstable(feature = "wrapping_next_power_of_two", issue = "32463",
                    reason = "needs decision on wrapping behaviour")]
         #[rustc_const_unstable(feature = "wrapping_next_power_of_two", issue = "32463")]
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index b1d36f27107..c603420f0f8 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -904,6 +904,7 @@ impl str {
     #[must_use = "this returns the split string as an iterator, \
                   without modifying the original"]
     #[stable(feature = "split_whitespace", since = "1.1.0")]
+    #[cfg_attr(not(test), rustc_diagnostic_item = "str_split_whitespace")]
     #[inline]
     pub fn split_whitespace(&self) -> SplitWhitespace<'_> {
         SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) }
@@ -1846,6 +1847,7 @@ impl str {
     #[must_use = "this returns the trimmed string as a slice, \
                   without modifying the original"]
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim")]
     pub fn trim(&self) -> &str {
         self.trim_matches(|c: char| c.is_whitespace())
     }
@@ -1884,6 +1886,7 @@ impl str {
     #[must_use = "this returns the trimmed string as a new slice, \
                   without modifying the original"]
     #[stable(feature = "trim_direction", since = "1.30.0")]
+    #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim_start")]
     pub fn trim_start(&self) -> &str {
         self.trim_start_matches(|c: char| c.is_whitespace())
     }
@@ -1922,6 +1925,7 @@ impl str {
     #[must_use = "this returns the trimmed string as a new slice, \
                   without modifying the original"]
     #[stable(feature = "trim_direction", since = "1.30.0")]
+    #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim_end")]
     pub fn trim_end(&self) -> &str {
         self.trim_end_matches(|c: char| c.is_whitespace())
     }
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index a810a57feb7..6d7ca9a94cf 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -568,7 +568,7 @@ fn configure_cmake(
     // We also do this if the user explicitly requested static libstdc++.
     if builder.config.llvm_static_stdcpp {
         if !target.contains("msvc") && !target.contains("netbsd") {
-            if target.contains("apple") {
+            if target.contains("apple") || target.contains("windows") {
                 ldflags.push_all("-static-libstdc++");
             } else {
                 ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 8db5f8b0cff..41efda980a2 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -399,7 +399,7 @@ fn run_test(
             eprint!("{}", self.0);
         }
     }
-    let mut out_lines = str::from_utf8(&output.stderr)
+    let mut out = str::from_utf8(&output.stderr)
         .unwrap()
         .lines()
         .filter(|l| {
@@ -410,15 +410,15 @@ fn run_test(
                 true
             }
         })
-        .collect::<Vec<_>>();
+        .intersperse_with(|| "\n")
+        .collect::<String>();
 
     // Add a \n to the end to properly terminate the last line,
     // but only if there was output to be printed
-    if !out_lines.is_empty() {
-        out_lines.push("");
+    if !out.is_empty() {
+        out.push('\n');
     }
 
-    let out = out_lines.join("\n");
     let _bomb = Bomb(&out);
     match (output.status.success(), lang_string.compile_fail) {
         (true, true) => {
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 3d8a62d50e0..5b14aca064e 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -487,12 +487,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
         module_id: DefId,
     ) -> Result<Res, ResolutionFailure<'a>> {
         self.cx.enter_resolver(|resolver| {
-            // NOTE: this needs 2 separate lookups because `resolve_str_path_error` doesn't take
+            // NOTE: this needs 2 separate lookups because `resolve_rustdoc_path` doesn't take
             // lexical scope into account (it ignores all macros not defined at the mod-level)
             debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
-            if let Ok((_, res)) =
-                resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id)
-            {
+            if let Some(res) = resolver.resolve_rustdoc_path(path_str, MacroNS, module_id) {
                 // don't resolve builtins like `#[derive]`
                 if let Ok(res) = res.try_into() {
                     return Ok(res);
@@ -540,10 +538,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
             })
     }
 
-    /// Convenience wrapper around `resolve_str_path_error`.
+    /// Convenience wrapper around `resolve_rustdoc_path`.
     ///
     /// This also handles resolving `true` and `false` as booleans.
-    /// NOTE: `resolve_str_path_error` knows only about paths, not about types.
+    /// NOTE: `resolve_rustdoc_path` knows only about paths, not about types.
     /// Associated items will never be resolved by this function.
     fn resolve_path(
         &self,
@@ -556,18 +554,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
             return res;
         }
 
-        let result = self.cx.enter_resolver(|resolver| {
-            resolver
-                .resolve_str_path_error(DUMMY_SP, path_str, ns, module_id)
-                .and_then(|(_, res)| res.try_into())
-        });
+        // Resolver doesn't know about true, false, and types that aren't paths (e.g. `()`).
+        let result = self
+            .cx
+            .enter_resolver(|resolver| resolver.resolve_rustdoc_path(path_str, ns, module_id))
+            .and_then(|res| res.try_into().ok())
+            .or_else(|| resolve_primitive(path_str, ns));
         debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
-        match result {
-            // resolver doesn't know about true, false, and types that aren't paths (e.g. `()`)
-            // manually as bool
-            Err(()) => resolve_primitive(path_str, ns),
-            Ok(res) => Some(res),
-        }
+        result
     }
 
     /// Resolves a string as a path within a particular namespace. Returns an
diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs
index 1d28bbde79c..30636faf98c 100644
--- a/src/librustdoc/passes/collect_intra_doc_links/early.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs
@@ -13,7 +13,7 @@ use rustc_hir::TraitCandidate;
 use rustc_middle::ty::{DefIdTree, Visibility};
 use rustc_resolve::{ParentScope, Resolver};
 use rustc_session::config::Externs;
-use rustc_span::{Span, SyntaxContext, DUMMY_SP};
+use rustc_span::SyntaxContext;
 
 use std::collections::hash_map::Entry;
 use std::mem;
@@ -39,7 +39,7 @@ crate fn early_resolve_intra_doc_links(
 
     // Overridden `visit_item` below doesn't apply to the crate root,
     // so we have to visit its attributes and reexports separately.
-    loader.load_links_in_attrs(&krate.attrs, krate.spans.inner_span);
+    loader.load_links_in_attrs(&krate.attrs);
     loader.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
     visit::walk_crate(&mut loader, krate);
     loader.add_foreign_traits_in_scope();
@@ -49,12 +49,7 @@ crate fn early_resolve_intra_doc_links(
     // DO NOT REMOVE THIS without first testing on the reproducer in
     // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
     for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) {
-        let _ = loader.resolver.resolve_str_path_error(
-            DUMMY_SP,
-            extern_name,
-            TypeNS,
-            CRATE_DEF_ID.to_def_id(),
-        );
+        loader.resolver.resolve_rustdoc_path(extern_name, TypeNS, CRATE_DEF_ID.to_def_id());
     }
 
     ResolverCaches {
@@ -151,7 +146,7 @@ impl IntraLinkCrateLoader<'_, '_> {
         }
     }
 
-    fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) {
+    fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute]) {
         // FIXME: this needs to consider reexport inlining.
         let attrs = clean::Attributes::from_ast(attrs, None);
         for (parent_module, doc) in attrs.collapsed_doc_value_by_module_level() {
@@ -165,7 +160,7 @@ impl IntraLinkCrateLoader<'_, '_> {
                 } else {
                     continue;
                 };
-                let _ = self.resolver.resolve_str_path_error(span, &path_str, TypeNS, module_id);
+                self.resolver.resolve_rustdoc_path(&path_str, TypeNS, module_id);
             }
         }
     }
@@ -201,7 +196,7 @@ impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> {
             // loaded, even if the module itself has no doc comments.
             self.add_traits_in_parent_scope(self.current_mod.to_def_id());
 
-            self.load_links_in_attrs(&item.attrs, item.span);
+            self.load_links_in_attrs(&item.attrs);
             self.process_module_children_or_reexports(self.current_mod.to_def_id());
             visit::walk_item(self, item);
 
@@ -216,28 +211,28 @@ impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> {
                 }
                 _ => {}
             }
-            self.load_links_in_attrs(&item.attrs, item.span);
+            self.load_links_in_attrs(&item.attrs);
             visit::walk_item(self, item);
         }
     }
 
     fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) {
-        self.load_links_in_attrs(&item.attrs, item.span);
+        self.load_links_in_attrs(&item.attrs);
         visit::walk_assoc_item(self, item, ctxt)
     }
 
     fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
-        self.load_links_in_attrs(&item.attrs, item.span);
+        self.load_links_in_attrs(&item.attrs);
         visit::walk_foreign_item(self, item)
     }
 
     fn visit_variant(&mut self, v: &ast::Variant) {
-        self.load_links_in_attrs(&v.attrs, v.span);
+        self.load_links_in_attrs(&v.attrs);
         visit::walk_variant(self, v)
     }
 
     fn visit_field_def(&mut self, field: &ast::FieldDef) {
-        self.load_links_in_attrs(&field.attrs, field.span);
+        self.load_links_in_attrs(&field.attrs);
         visit::walk_field_def(self, field)
     }
 
diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs
index 1a84f1a4479..e6afbe49864 100644
--- a/src/test/codegen/consts.rs
+++ b/src/test/codegen/consts.rs
@@ -10,7 +10,7 @@
 // CHECK: @STATIC = {{.*}}, align 4
 
 // This checks the constants from inline_enum_const
-// CHECK: @alloc9 = {{.*}}, align 2
+// CHECK: @alloc12 = {{.*}}, align 2
 
 // This checks the constants from {low,high}_align_const, they share the same
 // constant, but the alignment differs, so the higher one should be used
diff --git a/src/test/debuginfo/unsized.rs b/src/test/debuginfo/unsized.rs
index 7ccc88ef940..7cb0002ca51 100644
--- a/src/test/debuginfo/unsized.rs
+++ b/src/test/debuginfo/unsized.rs
@@ -16,13 +16,17 @@
 // gdbg-check:$3 = {pointer = [...], vtable = [...]}
 // gdbr-check:$3 = &unsized::Foo<dyn core::fmt::Debug> {pointer: [...], vtable: [...]}
 
+// gdb-command:print _box
+// gdbg-check:$4 = {pointer = [...], vtable = [...]}
+// gdbr-check:$4 = alloc::boxed::Box<unsized::Foo<dyn core::fmt::Debug>, alloc::alloc::Global> {pointer: [...], vtable: [...]}
+
 // gdb-command:print tuple_slice
-// gdbg-check:$4 = {data_ptr = [...], length = 2}
-// gdbr-check:$4 = &(i32, i32, [i32]) {data_ptr: [...], length: 2}
+// gdbg-check:$5 = {data_ptr = [...], length = 2}
+// gdbr-check:$5 = &(i32, i32, [i32]) {data_ptr: [...], length: 2}
 
 // gdb-command:print tuple_dyn
-// gdbg-check:$5 = {pointer = [...], vtable = [...]}
-// gdbr-check:$5 = &(i32, i32, dyn core::fmt::Debug) {pointer: [...], vtable: [...]}
+// gdbg-check:$6 = {pointer = [...], vtable = [...]}
+// gdbr-check:$6 = &(i32, i32, dyn core::fmt::Debug) {pointer: [...], vtable: [...]}
 
 // === CDB TESTS ===================================================================================
 
@@ -42,6 +46,12 @@
 // cdb-check:    [+0x000] pointer          : 0x[...] [Type: unsized::Foo<dyn$<core::fmt::Debug> > *]
 // cdb-check:    [...] vtable           : 0x[...] [Type: unsigned [...]int[...] (*)[3]]
 
+// cdb-command:dx _box
+// cdb-check:
+// cdb-check:_box             [Type: alloc::boxed::Box<unsized::Foo<dyn$<core::fmt::Debug> >,alloc::alloc::Global>]
+// cdb-check:[+0x000] pointer          : 0x[...] [Type: unsized::Foo<dyn$<core::fmt::Debug> > *]
+// cdb-check:[...] vtable           : 0x[...] [Type: unsigned [...]int[...] (*)[3]]
+
 // cdb-command:dx tuple_slice
 // cdb-check:tuple_slice      [Type: ref$<tuple$<i32,i32,slice$<i32> > >]
 // cdb-check:    [+0x000] data_ptr         : 0x[...] [Type: tuple$<i32,i32,slice$<i32> > *]
@@ -69,6 +79,7 @@ fn main() {
     let a: &Foo<[u8]> = &foo.value;
     let b: &Foo<Foo<[u8]>> = &foo;
     let c: &Foo<dyn std::fmt::Debug> = &Foo { value: 7i32 };
+    let _box: Box<Foo<dyn std::fmt::Debug>> = Box::new(Foo { value: 8i32 });
 
     // Also check unsized tuples
     let tuple_slice: &(i32, i32, [i32]) = &(0, 1, [2, 3]);
diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs
index be8162c86b9..0315938a7ed 100644
--- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs
+++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs
@@ -4,12 +4,12 @@ trait Foo {
     const BAR: u32;
 }
 
-const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; //~ ERROR E0391
+const IMPL_REF_BAR: u32 = GlobalImplRef::BAR;
 
 struct GlobalImplRef;
 
 impl GlobalImplRef {
-    const BAR: u32 = IMPL_REF_BAR;
+    const BAR: u32 = IMPL_REF_BAR; //~ ERROR E0391
 }
 
 fn main() {}
diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr
index 61b16cb9d58..3d696bb049c 100644
--- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr
+++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr
@@ -1,9 +1,15 @@
-error[E0391]: cycle detected when simplifying constant for the type system `IMPL_REF_BAR`
+error[E0391]: cycle detected when elaborating drops for `<impl at $DIR/issue-24949-assoc-const-static-recursion-impl.rs:11:1: 13:2>::BAR`
+  --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5
+   |
+LL |     const BAR: u32 = IMPL_REF_BAR;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ...which requires normalizing `IMPL_REF_BAR`...
+note: ...which requires simplifying constant for the type system `IMPL_REF_BAR`...
   --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1
    |
 LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
 note: ...which requires simplifying constant for the type system `IMPL_REF_BAR`...
   --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1
    |
@@ -35,8 +41,7 @@ note: ...which requires caching mir of `<impl at $DIR/issue-24949-assoc-const-st
    |
 LL |     const BAR: u32 = IMPL_REF_BAR;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: ...which requires normalizing `IMPL_REF_BAR`...
-   = note: ...which again requires simplifying constant for the type system `IMPL_REF_BAR`, completing the cycle
+   = note: ...which again requires elaborating drops for `<impl at $DIR/issue-24949-assoc-const-static-recursion-impl.rs:11:1: 13:2>::BAR`, completing the cycle
    = note: cycle used when running analysis passes on this crate
 
 error: aborting due to previous error
diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs
index cec75ae19f4..4e89f686288 100644
--- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs
+++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs
@@ -5,10 +5,10 @@ trait Foo {
 }
 
 trait FooDefault {
-    const BAR: u32 = DEFAULT_REF_BAR;
+    const BAR: u32 = DEFAULT_REF_BAR; //~ ERROR E0391
 }
 
-const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR; //~ ERROR E0391
+const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR;
 
 struct GlobalDefaultRef;
 
diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr
index 494dc0c0ed4..71e26245e16 100644
--- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr
+++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr
@@ -1,9 +1,15 @@
-error[E0391]: cycle detected when simplifying constant for the type system `DEFAULT_REF_BAR`
+error[E0391]: cycle detected when elaborating drops for `FooDefault::BAR`
+  --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5
+   |
+LL |     const BAR: u32 = DEFAULT_REF_BAR;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ...which requires normalizing `DEFAULT_REF_BAR`...
+note: ...which requires simplifying constant for the type system `DEFAULT_REF_BAR`...
   --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1
    |
 LL | const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
 note: ...which requires simplifying constant for the type system `DEFAULT_REF_BAR`...
   --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1
    |
@@ -35,8 +41,7 @@ note: ...which requires caching mir of `FooDefault::BAR` for CTFE...
    |
 LL |     const BAR: u32 = DEFAULT_REF_BAR;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: ...which requires normalizing `DEFAULT_REF_BAR`...
-   = note: ...which again requires simplifying constant for the type system `DEFAULT_REF_BAR`, completing the cycle
+   = note: ...which again requires elaborating drops for `FooDefault::BAR`, completing the cycle
    = note: cycle used when running analysis passes on this crate
 
 error: aborting due to previous error
diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs
index 62af8534340..68b653ff3c5 100644
--- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs
+++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs
@@ -4,12 +4,12 @@ trait Foo {
     const BAR: u32;
 }
 
-const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR; //~ ERROR E0391
+const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR;
 
 struct GlobalTraitRef;
 
 impl Foo for GlobalTraitRef {
-    const BAR: u32 = TRAIT_REF_BAR;
+    const BAR: u32 = TRAIT_REF_BAR; //~ ERROR E0391
 }
 
 fn main() {}
diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr
index 4ff253bffcb..020d758e153 100644
--- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr
+++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr
@@ -1,9 +1,15 @@
-error[E0391]: cycle detected when simplifying constant for the type system `TRAIT_REF_BAR`
+error[E0391]: cycle detected when elaborating drops for `<impl at $DIR/issue-24949-assoc-const-static-recursion-trait.rs:11:1: 13:2>::BAR`
+  --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5
+   |
+LL |     const BAR: u32 = TRAIT_REF_BAR;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ...which requires normalizing `TRAIT_REF_BAR`...
+note: ...which requires simplifying constant for the type system `TRAIT_REF_BAR`...
   --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1
    |
 LL | const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
 note: ...which requires simplifying constant for the type system `TRAIT_REF_BAR`...
   --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1
    |
@@ -35,8 +41,7 @@ note: ...which requires caching mir of `<impl at $DIR/issue-24949-assoc-const-st
    |
 LL |     const BAR: u32 = TRAIT_REF_BAR;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: ...which requires normalizing `TRAIT_REF_BAR`...
-   = note: ...which again requires simplifying constant for the type system `TRAIT_REF_BAR`, completing the cycle
+   = note: ...which again requires elaborating drops for `<impl at $DIR/issue-24949-assoc-const-static-recursion-trait.rs:11:1: 13:2>::BAR`, completing the cycle
    = note: cycle used when running analysis passes on this crate
 
 error: aborting due to previous error
diff --git a/src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs b/src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs
new file mode 100644
index 00000000000..39ccaa6ac35
--- /dev/null
+++ b/src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs
@@ -0,0 +1,25 @@
+// check-pass
+
+#![feature(negative_impls)]
+#![feature(rustc_attrs)]
+#![feature(with_negative_coherence)]
+
+trait Foo {}
+
+impl !Foo for u32 {}
+
+#[rustc_strict_coherence]
+struct MyStruct<T>(T);
+
+impl MyStruct<u32> {
+    fn method(&self) {}
+}
+
+impl<T> MyStruct<T>
+where
+    T: Foo,
+{
+    fn method(&self) {}
+}
+
+fn main() {}
diff --git a/src/test/ui/coherence/coherence-negative-inherent.rs b/src/test/ui/coherence/coherence-negative-inherent.rs
new file mode 100644
index 00000000000..a9e1acc8044
--- /dev/null
+++ b/src/test/ui/coherence/coherence-negative-inherent.rs
@@ -0,0 +1,22 @@
+// check-pass
+
+#![feature(negative_impls)]
+#![feature(rustc_attrs)]
+#![feature(with_negative_coherence)]
+
+#[rustc_strict_coherence]
+trait Foo {}
+
+impl !Foo for u32 {}
+
+struct MyStruct<T>(T);
+
+impl<T: Foo> MyStruct<T> {
+    fn method(&self) {}
+}
+
+impl MyStruct<u32> {
+    fn method(&self) {}
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs
new file mode 100644
index 00000000000..c59d62e576d
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs
@@ -0,0 +1,26 @@
+// build-pass
+
+#![feature(generic_const_exprs)]
+//~^ WARNING the feature `generic_const_exprs` is incomplete
+
+trait Generic {
+    const ASSOC: usize;
+}
+
+impl Generic for u8 {
+    const ASSOC: usize = 17;
+}
+impl Generic for u16 {
+    const ASSOC: usize = 13;
+}
+
+
+fn uses_assoc_type<T: Generic, const N: usize>() -> [u8; N + T::ASSOC] {
+    [0; N + T::ASSOC]
+}
+
+fn only_generic_n<const N: usize>() -> [u8; N + 13] {
+    uses_assoc_type::<u16, N>()
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr
new file mode 100644
index 00000000000..b5719b3fe1d
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr
@@ -0,0 +1,11 @@
+warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/eval-try-unify.rs:3:12
+   |
+LL | #![feature(generic_const_exprs)]
+   |            ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs
index 68536348d38..71c164ab0a5 100644
--- a/src/test/ui/const-generics/issues/issue-83765.rs
+++ b/src/test/ui/const-generics/issues/issue-83765.rs
@@ -2,114 +2,115 @@
 #![allow(incomplete_features)]
 
 trait TensorDimension {
-    const DIM : usize;
-    const ISSCALAR : bool = Self::DIM == 0;
-    fn is_scalar(&self) -> bool {Self::ISSCALAR}
+    const DIM: usize;
+    //~^ ERROR cycle detected when resolving instance
+    // FIXME Given the current state of the compiler its expected that we cycle here,
+    // but the cycle is still wrong.
+    const ISSCALAR: bool = Self::DIM == 0;
+    fn is_scalar(&self) -> bool {
+        Self::ISSCALAR
+    }
 }
 
-trait TensorSize : TensorDimension {
-    fn size(&self) -> [usize;Self::DIM];
-    fn inbounds(&self,index : [usize;Self::DIM]) -> bool {
-        index.iter().zip(self.size().iter()).all(|(i,s)| i < s)
+trait TensorSize: TensorDimension {
+    fn size(&self) -> [usize; Self::DIM];
+    fn inbounds(&self, index: [usize; Self::DIM]) -> bool {
+        index.iter().zip(self.size().iter()).all(|(i, s)| i < s)
     }
 }
 
-
 trait Broadcastable: TensorSize + Sized {
     type Element;
-    fn bget(&self, index:[usize;Self::DIM]) -> Option<Self::Element>;
-    fn lazy_updim<const NEWDIM : usize>(&self, size : [usize;NEWDIM] ) ->
-       LazyUpdim<Self,{Self::DIM},NEWDIM>
-    {
-        assert!(NEWDIM >= Self::DIM,
-            "Updimmed tensor cannot have fewer indices than the initial one.");
-        LazyUpdim {size,reference:&self}
+    fn bget(&self, index: [usize; Self::DIM]) -> Option<Self::Element>;
+    fn lazy_updim<const NEWDIM: usize>(
+        &self,
+        size: [usize; NEWDIM],
+    ) -> LazyUpdim<Self, { Self::DIM }, NEWDIM> {
+        assert!(
+            NEWDIM >= Self::DIM,
+            "Updimmed tensor cannot have fewer indices than the initial one."
+        );
+        LazyUpdim { size, reference: &self }
     }
-    fn bmap<T,F :Fn(Self::Element) -> T>(&self,foo : F) -> BMap<T,Self,F,{Self::DIM}>{
-        BMap {reference:self,closure : foo}
+    fn bmap<T, F: Fn(Self::Element) -> T>(&self, foo: F) -> BMap<T, Self, F, { Self::DIM }> {
+        BMap { reference: self, closure: foo }
     }
 }
 
-
-struct LazyUpdim<'a,T : Broadcastable,const OLDDIM : usize, const DIM : usize> {
-    size : [usize;DIM],
-    reference : &'a T
+struct LazyUpdim<'a, T: Broadcastable, const OLDDIM: usize, const DIM: usize> {
+    size: [usize; DIM],
+    reference: &'a T,
 }
 
-impl<'a,T : Broadcastable,const DIM : usize> TensorDimension for LazyUpdim<'a,T,{T::DIM},DIM> {
-    const DIM : usize = DIM;
+impl<'a, T: Broadcastable, const DIM: usize> TensorDimension for LazyUpdim<'a, T, { T::DIM }, DIM> {
+    const DIM: usize = DIM;
 }
 
-impl<'a,T : Broadcastable,const DIM : usize> TensorSize for LazyUpdim<'a,T,{T::DIM},DIM> {
-    fn size(&self) -> [usize;DIM] {self.size}
-    //~^ ERROR method not compatible with trait
+impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T::DIM }, DIM> {
+    fn size(&self) -> [usize; DIM] {
+        self.size
+    }
 }
 
-impl<'a,T : Broadcastable,const DIM : usize>  Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM>
-{
+impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> {
     type Element = T::Element;
-    fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> {
-      //~^ ERROR method not compatible with trait
+    fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> {
         assert!(DIM >= T::DIM);
-        if !self.inbounds(index) {return None}
-        //~^ ERROR unconstrained generic constant
-        //~| ERROR mismatched types
+        if !self.inbounds(index) {
+            return None;
+        }
         let size = self.size();
-        //~^ ERROR unconstrained generic constant
-        let newindex : [usize;T::DIM] = Default::default();
-        //~^ ERROR the trait bound `[usize; _]: Default` is not satisfied
+        let newindex: [usize; T::DIM] = Default::default();
         self.reference.bget(newindex)
     }
 }
 
-struct BMap<'a,R, T : Broadcastable, F :  Fn(T::Element) -> R  , const DIM: usize> {
-    reference : &'a T,
-    closure : F
+struct BMap<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> {
+    reference: &'a T,
+    closure: F,
 }
 
-impl<'a,R, T : Broadcastable, F :  Fn(T::Element) -> R,
-     const DIM: usize> TensorDimension for BMap<'a,R,T,F,DIM> {
-
-    const DIM : usize = DIM;
+impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorDimension
+    for BMap<'a, R, T, F, DIM>
+{
+    const DIM: usize = DIM;
 }
-impl<'a,R, T : Broadcastable, F :  Fn(T::Element) -> R  ,
-      const DIM: usize> TensorSize for BMap<'a,R,T,F,DIM> {
-
-    fn size(&self) -> [usize;DIM] {self.reference.size()}
-    //~^ ERROR unconstrained generic constant
-    //~| ERROR mismatched types
-    //~| ERROR method not compatible with trait
+impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorSize
+    for BMap<'a, R, T, F, DIM>
+{
+    fn size(&self) -> [usize; DIM] {
+        self.reference.size()
+    }
 }
 
-impl<'a,R, T : Broadcastable, F :  Fn(T::Element) -> R  ,
-  const DIM: usize> Broadcastable for BMap<'a,R,T,F,DIM> {
-
+impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> Broadcastable
+    for BMap<'a, R, T, F, DIM>
+{
     type Element = R;
-    fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> {
-      //~^ ERROR method not compatible with trait
+    fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> {
         self.reference.bget(index).map(&self.closure)
-        //~^ ERROR unconstrained generic constant
-        //~| ERROR mismatched types
     }
 }
 
 impl<T> TensorDimension for Vec<T> {
-    const DIM : usize = 1;
+    const DIM: usize = 1;
 }
 impl<T> TensorSize for Vec<T> {
-    fn size(&self) -> [usize;1] {[self.len()]}
+    fn size(&self) -> [usize; 1] {
+        [self.len()]
+    }
 }
 impl<T: Clone> Broadcastable for Vec<T> {
     type Element = T;
-    fn bget(& self,index : [usize;1]) -> Option<T> {
+    fn bget(&self, index: [usize; 1]) -> Option<T> {
         self.get(index[0]).cloned()
     }
 }
 
 fn main() {
-    let v = vec![1,2,3];
-    let bv = v.lazy_updim([3,4]);
-    let bbv = bv.bmap(|x| x*x);
+    let v = vec![1, 2, 3];
+    let bv = v.lazy_updim([3, 4]);
+    let bbv = bv.bmap(|x| x * x);
 
-    println!("The size of v is {:?}",bbv.bget([0,2]).expect("Out of bounds."));
+    println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds."));
 }
diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr
index a49f850717f..8705a39fa4b 100644
--- a/src/test/ui/const-generics/issues/issue-83765.stderr
+++ b/src/test/ui/const-generics/issues/issue-83765.stderr
@@ -1,130 +1,17 @@
-error[E0308]: method not compatible with trait
-  --> $DIR/issue-83765.rs:44:5
+error[E0391]: cycle detected when resolving instance `<LazyUpdim<T, { T::DIM }, DIM> as TensorDimension>::DIM`
+  --> $DIR/issue-83765.rs:5:5
    |
-LL |     fn size(&self) -> [usize;DIM] {self.size}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
+LL |     const DIM: usize;
+   |     ^^^^^^^^^^^^^^^^^
    |
-   = note: expected type `Self::DIM`
-              found type `DIM`
-
-error[E0308]: method not compatible with trait
-  --> $DIR/issue-83765.rs:51:5
-   |
-LL |     fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
-   |
-   = note: expected type `Self::DIM`
-              found type `DIM`
-
-error[E0308]: method not compatible with trait
-  --> $DIR/issue-83765.rs:78:5
-   |
-LL |     fn size(&self) -> [usize;DIM] {self.reference.size()}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
-   |
-   = note: expected type `Self::DIM`
-              found type `DIM`
-
-error[E0308]: method not compatible with trait
-  --> $DIR/issue-83765.rs:88:5
-   |
-LL |     fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
-   |
-   = note: expected type `Self::DIM`
-              found type `DIM`
-
-error: unconstrained generic constant
-  --> $DIR/issue-83765.rs:54:18
-   |
-LL |         if !self.inbounds(index) {return None}
-   |                  ^^^^^^^^
-   |
-   = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:`
-note: required by a bound in `TensorSize::inbounds`
-  --> $DIR/issue-83765.rs:12:38
-   |
-LL |     fn inbounds(&self,index : [usize;Self::DIM]) -> bool {
-   |                                      ^^^^^^^^^ required by this bound in `TensorSize::inbounds`
-
-error[E0308]: mismatched types
-  --> $DIR/issue-83765.rs:54:27
-   |
-LL |         if !self.inbounds(index) {return None}
-   |                           ^^^^^ expected `Self::DIM`, found `DIM`
-   |
-   = note: expected type `Self::DIM`
-              found type `DIM`
-
-error: unconstrained generic constant
-  --> $DIR/issue-83765.rs:57:25
-   |
-LL |         let size = self.size();
-   |                         ^^^^
-   |
-   = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:`
-note: required by a bound in `TensorSize::size`
-  --> $DIR/issue-83765.rs:11:30
-   |
-LL |     fn size(&self) -> [usize;Self::DIM];
-   |                              ^^^^^^^^^ required by this bound in `TensorSize::size`
-
-error[E0277]: the trait bound `[usize; _]: Default` is not satisfied
-  --> $DIR/issue-83765.rs:59:41
-   |
-LL |         let newindex : [usize;T::DIM] = Default::default();
-   |                                         ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[usize; _]`
-   |
-help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
-   |
-LL | impl<'a,T : Broadcastable,const DIM : usize>  Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> where [usize; _]: Default
-   |                                                                                              +++++++++++++++++++++++++
-
-error: unconstrained generic constant
-  --> $DIR/issue-83765.rs:78:51
-   |
-LL |     fn size(&self) -> [usize;DIM] {self.reference.size()}
-   |                                                   ^^^^
-   |
-   = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:`
-note: required by a bound in `TensorSize::size`
-  --> $DIR/issue-83765.rs:11:30
-   |
-LL |     fn size(&self) -> [usize;Self::DIM];
-   |                              ^^^^^^^^^ required by this bound in `TensorSize::size`
-
-error[E0308]: mismatched types
-  --> $DIR/issue-83765.rs:78:36
-   |
-LL |     fn size(&self) -> [usize;DIM] {self.reference.size()}
-   |                                    ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM`
-   |
-   = note: expected type `DIM`
-              found type `Self::DIM`
-
-error: unconstrained generic constant
-  --> $DIR/issue-83765.rs:90:24
-   |
-LL |         self.reference.bget(index).map(&self.closure)
-   |                        ^^^^
-   |
-   = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:`
-note: required by a bound in `Broadcastable::bget`
-  --> $DIR/issue-83765.rs:20:33
-   |
-LL |     fn bget(&self, index:[usize;Self::DIM]) -> Option<Self::Element>;
-   |                                 ^^^^^^^^^ required by this bound in `Broadcastable::bget`
-
-error[E0308]: mismatched types
-  --> $DIR/issue-83765.rs:90:29
-   |
-LL |         self.reference.bget(index).map(&self.closure)
-   |                             ^^^^^ expected `Self::DIM`, found `DIM`
+note: ...which requires checking if `TensorDimension` fulfills its obligations...
+  --> $DIR/issue-83765.rs:4:1
    |
-   = note: expected type `Self::DIM`
-              found type `DIM`
+LL | trait TensorDimension {
+   | ^^^^^^^^^^^^^^^^^^^^^
+   = note: ...which again requires resolving instance `<LazyUpdim<T, { T::DIM }, DIM> as TensorDimension>::DIM`, completing the cycle
+   = note: cycle used when normalizing `<LazyUpdim<T, { T::DIM }, DIM> as TensorDimension>::DIM`
 
-error: aborting due to 12 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0277, E0308.
-For more information about an error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0391`.
diff --git a/src/test/ui/const_prop/inline_spans.rs b/src/test/ui/const_prop/inline_spans.rs
index f1cfcfee557..504f2781156 100644
--- a/src/test/ui/const_prop/inline_spans.rs
+++ b/src/test/ui/const_prop/inline_spans.rs
@@ -1,18 +1,15 @@
-// build-fail
+// build-pass
 // compile-flags: -Zmir-opt-level=3
+// Overflow can't be detected by const prop
+// could only be detected after optimizations
 
 #![deny(warnings)]
 
 fn main() {
     let _ = add(u8::MAX, 1);
-    //~^ NOTE in this expansion of inlined source
-    //~| NOTE in this expansion of inlined source
 }
 
 #[inline(always)]
 fn add(x: u8, y: u8) -> u8 {
     x + y
-    //~^ ERROR this arithmetic operation will overflow
-    //~| NOTE attempt to compute `u8::MAX + 1_u8`, which would overflow
-    //~| NOTE `#[deny(arithmetic_overflow)]` on by default
 }
diff --git a/src/test/ui/const_prop/inline_spans.stderr b/src/test/ui/const_prop/inline_spans.stderr
deleted file mode 100644
index f99a3142386..00000000000
--- a/src/test/ui/const_prop/inline_spans.stderr
+++ /dev/null
@@ -1,13 +0,0 @@
-error: this arithmetic operation will overflow
-  --> $DIR/inline_spans.rs:14:5
-   |
-LL |     let _ = add(u8::MAX, 1);
-   |             --------------- in this inlined function call
-...
-LL |     x + y
-   |     ^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow
-   |
-   = note: `#[deny(arithmetic_overflow)]` on by default
-
-error: aborting due to previous error
-
diff --git a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr
index 45a3d901c98..8bd5c08dc46 100644
--- a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr
+++ b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr
@@ -21,6 +21,7 @@ LL |     let x: &'static i32 = &X;
    |                            ^ referenced constant has errors
 query stack during panic:
 #0 [try_normalize_mir_const_after_erasing_regions] normalizing `main::promoted[1]`
-#1 [optimized_mir] optimizing MIR for `main`
-#2 [collect_and_partition_mono_items] collect_and_partition_mono_items
+#1 [mir_drops_elaborated_and_const_checked] elaborating drops for `main`
+#2 [optimized_mir] optimizing MIR for `main`
+#3 [collect_and_partition_mono_items] collect_and_partition_mono_items
 end of query stack
diff --git a/src/test/ui/consts/const-eval/issue-49296.stderr b/src/test/ui/consts/const-eval/issue-49296.stderr
index 1864a284579..cc4f1594c32 100644
--- a/src/test/ui/consts/const-eval/issue-49296.stderr
+++ b/src/test/ui/consts/const-eval/issue-49296.stderr
@@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed
   --> $DIR/issue-49296.rs:9:16
    |
 LL | const X: u64 = *wat(42);
-   |                ^^^^^^^^ pointer to alloc2 was dereferenced after this allocation got freed
+   |                ^^^^^^^^ pointer to alloc3 was dereferenced after this allocation got freed
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/consts/const-eval/panic-assoc-never-type.rs b/src/test/ui/consts/const-eval/panic-assoc-never-type.rs
index 2bdf5d54765..80b0a1432fb 100644
--- a/src/test/ui/consts/const-eval/panic-assoc-never-type.rs
+++ b/src/test/ui/consts/const-eval/panic-assoc-never-type.rs
@@ -13,4 +13,5 @@ impl PrintName {
 
 fn main() {
     let _ = PrintName::VOID;
+    //~^ ERROR erroneous constant used [E0080]
 }
diff --git a/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr b/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr
index 9631b7748b5..0116a83910d 100644
--- a/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr
+++ b/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr
@@ -6,6 +6,12 @@ LL |     const VOID: ! = panic!();
    |
    = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to previous error
+error[E0080]: erroneous constant used
+  --> $DIR/panic-assoc-never-type.rs:15:13
+   |
+LL |     let _ = PrintName::VOID;
+   |             ^^^^^^^^^^^^^^^ referenced constant has errors
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr b/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr
index 77e7d484071..5bfd4ef92a9 100644
--- a/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr
+++ b/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr
@@ -1,3 +1,45 @@
+warning: this arithmetic operation will overflow
+  --> $DIR/promoted_errors.rs:15:5
+   |
+LL |     0 - 1
+   |     ^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow
+   |
+note: the lint level is defined here
+  --> $DIR/promoted_errors.rs:11:20
+   |
+LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
+   |                    ^^^^^^^^^^^^^^^^^^^
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:21:5
+   |
+LL |     1 / 0
+   |     ^^^^^ attempt to divide `1_i32` by zero
+   |
+note: the lint level is defined here
+  --> $DIR/promoted_errors.rs:11:41
+   |
+LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
+   |                                         ^^^^^^^^^^^^^^^^^^^
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:27:5
+   |
+LL |     1 / (1 - 1)
+   |     ^^^^^^^^^^^ attempt to divide `1_i32` by zero
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:31:5
+   |
+LL |     1 / (false as i32)
+   |     ^^^^^^^^^^^^^^^^^^ attempt to divide `1_i32` by zero
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:35:5
+   |
+LL |     [1, 2, 3][4]
+   |     ^^^^^^^^^^^^ index out of bounds: the length is 3 but the index is 4
+
 warning: any use of this value will cause an error
   --> $DIR/promoted_errors.rs:15:5
    |
@@ -6,7 +48,7 @@ LL |       0 - 1
    |       |
    |       attempt to compute `0_u32 - 1_u32`, which would overflow
    |       inside `overflow` at $DIR/promoted_errors.rs:15:5
-   |       inside `X` at $DIR/promoted_errors.rs:38:29
+   |       inside `X` at $DIR/promoted_errors.rs:43:29
 ...
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
@@ -26,7 +68,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
    = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
 
 warning: any use of this value will cause an error
-  --> $DIR/promoted_errors.rs:38:28
+  --> $DIR/promoted_errors.rs:43:28
    |
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
@@ -41,5 +83,5 @@ LL | | };
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
 
-warning: 2 warnings emitted
+warning: 7 warnings emitted
 
diff --git a/src/test/ui/consts/const-eval/promoted_errors.opt.stderr b/src/test/ui/consts/const-eval/promoted_errors.opt.stderr
index 6b17346e6ec..0a8a8aef3cf 100644
--- a/src/test/ui/consts/const-eval/promoted_errors.opt.stderr
+++ b/src/test/ui/consts/const-eval/promoted_errors.opt.stderr
@@ -1,12 +1,54 @@
+warning: this arithmetic operation will overflow
+  --> $DIR/promoted_errors.rs:15:5
+   |
+LL |     0 - 1
+   |     ^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow
+   |
+note: the lint level is defined here
+  --> $DIR/promoted_errors.rs:11:20
+   |
+LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
+   |                    ^^^^^^^^^^^^^^^^^^^
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:21:5
+   |
+LL |     1 / 0
+   |     ^^^^^ attempt to divide `1_i32` by zero
+   |
+note: the lint level is defined here
+  --> $DIR/promoted_errors.rs:11:41
+   |
+LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
+   |                                         ^^^^^^^^^^^^^^^^^^^
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:27:5
+   |
+LL |     1 / (1 - 1)
+   |     ^^^^^^^^^^^ attempt to divide `1_i32` by zero
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:31:5
+   |
+LL |     1 / (false as i32)
+   |     ^^^^^^^^^^^^^^^^^^ attempt to divide `1_i32` by zero
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:35:5
+   |
+LL |     [1, 2, 3][4]
+   |     ^^^^^^^^^^^^ index out of bounds: the length is 3 but the index is 4
+
 warning: any use of this value will cause an error
-  --> $DIR/promoted_errors.rs:20:5
+  --> $DIR/promoted_errors.rs:21:5
    |
 LL |       1 / 0
    |       ^^^^^
    |       |
    |       attempt to divide `1_i32` by zero
-   |       inside `div_by_zero1` at $DIR/promoted_errors.rs:20:5
-   |       inside `X` at $DIR/promoted_errors.rs:41:29
+   |       inside `div_by_zero1` at $DIR/promoted_errors.rs:21:5
+   |       inside `X` at $DIR/promoted_errors.rs:46:29
 ...
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
@@ -26,7 +68,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
    = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
 
 warning: any use of this value will cause an error
-  --> $DIR/promoted_errors.rs:41:28
+  --> $DIR/promoted_errors.rs:46:28
    |
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
@@ -42,5 +84,5 @@ LL | | };
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
 
-warning: 2 warnings emitted
+warning: 7 warnings emitted
 
diff --git a/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr b/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr
index 77e7d484071..5bfd4ef92a9 100644
--- a/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr
+++ b/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr
@@ -1,3 +1,45 @@
+warning: this arithmetic operation will overflow
+  --> $DIR/promoted_errors.rs:15:5
+   |
+LL |     0 - 1
+   |     ^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow
+   |
+note: the lint level is defined here
+  --> $DIR/promoted_errors.rs:11:20
+   |
+LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
+   |                    ^^^^^^^^^^^^^^^^^^^
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:21:5
+   |
+LL |     1 / 0
+   |     ^^^^^ attempt to divide `1_i32` by zero
+   |
+note: the lint level is defined here
+  --> $DIR/promoted_errors.rs:11:41
+   |
+LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
+   |                                         ^^^^^^^^^^^^^^^^^^^
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:27:5
+   |
+LL |     1 / (1 - 1)
+   |     ^^^^^^^^^^^ attempt to divide `1_i32` by zero
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:31:5
+   |
+LL |     1 / (false as i32)
+   |     ^^^^^^^^^^^^^^^^^^ attempt to divide `1_i32` by zero
+
+warning: this operation will panic at runtime
+  --> $DIR/promoted_errors.rs:35:5
+   |
+LL |     [1, 2, 3][4]
+   |     ^^^^^^^^^^^^ index out of bounds: the length is 3 but the index is 4
+
 warning: any use of this value will cause an error
   --> $DIR/promoted_errors.rs:15:5
    |
@@ -6,7 +48,7 @@ LL |       0 - 1
    |       |
    |       attempt to compute `0_u32 - 1_u32`, which would overflow
    |       inside `overflow` at $DIR/promoted_errors.rs:15:5
-   |       inside `X` at $DIR/promoted_errors.rs:38:29
+   |       inside `X` at $DIR/promoted_errors.rs:43:29
 ...
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
@@ -26,7 +68,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
    = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
 
 warning: any use of this value will cause an error
-  --> $DIR/promoted_errors.rs:38:28
+  --> $DIR/promoted_errors.rs:43:28
    |
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
@@ -41,5 +83,5 @@ LL | | };
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
 
-warning: 2 warnings emitted
+warning: 7 warnings emitted
 
diff --git a/src/test/ui/consts/const-eval/promoted_errors.rs b/src/test/ui/consts/const-eval/promoted_errors.rs
index 5bafea1ed46..eb891de33c4 100644
--- a/src/test/ui/consts/const-eval/promoted_errors.rs
+++ b/src/test/ui/consts/const-eval/promoted_errors.rs
@@ -15,20 +15,25 @@ const fn overflow() -> u32 {
     0 - 1
     //[opt_with_overflow_checks,noopt]~^ WARN any use of this value will cause an error
     //[opt_with_overflow_checks,noopt]~| WARN this was previously accepted by the compiler
+    //~^^^ WARN this arithmetic operation will overflow
 }
 const fn div_by_zero1() -> i32 {
     1 / 0
     //[opt]~^ WARN any use of this value will cause an error
     //[opt]~| WARN this was previously accepted by the compiler but is being phased out
+    //~^^^ WARN this operation will panic at runtime
 }
 const fn div_by_zero2() -> i32 {
     1 / (1 - 1)
+    //~^ WARN this operation will panic at runtime
 }
 const fn div_by_zero3() -> i32 {
     1 / (false as i32)
+    //~^ WARN this operation will panic at runtime
 }
 const fn oob() -> i32 {
     [1, 2, 3][4]
+    //~^ WARN this operation will panic at runtime
 }
 
 // An unused constant containing failing promoteds.
diff --git a/src/test/ui/consts/const-eval/union-const-eval-field.rs b/src/test/ui/consts/const-eval/union-const-eval-field.rs
index 80263718330..a1e48cac4fa 100644
--- a/src/test/ui/consts/const-eval/union-const-eval-field.rs
+++ b/src/test/ui/consts/const-eval/union-const-eval-field.rs
@@ -28,6 +28,7 @@ const fn read_field3() -> Field3 {
     const FIELD3: Field3 = unsafe { UNION.field3 };
     //~^ ERROR it is undefined behavior to use this value
     FIELD3
+    //~^ ERROR erroneous constant used [E0080]
 }
 
 fn main() {
diff --git a/src/test/ui/consts/const-eval/union-const-eval-field.stderr b/src/test/ui/consts/const-eval/union-const-eval-field.stderr
index e5a107ff011..8f818462781 100644
--- a/src/test/ui/consts/const-eval/union-const-eval-field.stderr
+++ b/src/test/ui/consts/const-eval/union-const-eval-field.stderr
@@ -9,6 +9,12 @@ LL |     const FIELD3: Field3 = unsafe { UNION.field3 };
                __ __ __ __ __ __ __ __                         │ ░░░░░░░░
            }
 
-error: aborting due to previous error
+error[E0080]: erroneous constant used
+  --> $DIR/union-const-eval-field.rs:30:5
+   |
+LL |     FIELD3
+   |     ^^^^^^ referenced constant has errors
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/issue-56164.rs b/src/test/ui/consts/issue-56164.rs
index 22ac75514f6..094ca377e03 100644
--- a/src/test/ui/consts/issue-56164.rs
+++ b/src/test/ui/consts/issue-56164.rs
@@ -1,5 +1,7 @@
 const fn foo() { (||{})() }
 //~^ ERROR cannot call non-const closure
+//~| ERROR erroneous constant used [const_err]
+//~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 const fn bad(input: fn()) {
     input()
diff --git a/src/test/ui/consts/issue-56164.stderr b/src/test/ui/consts/issue-56164.stderr
index 803424eedf3..b997aff0e83 100644
--- a/src/test/ui/consts/issue-56164.stderr
+++ b/src/test/ui/consts/issue-56164.stderr
@@ -8,11 +8,21 @@ LL | const fn foo() { (||{})() }
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
 
 error: function pointers are not allowed in const fn
-  --> $DIR/issue-56164.rs:5:5
+  --> $DIR/issue-56164.rs:7:5
    |
 LL |     input()
    |     ^^^^^^^
 
-error: aborting due to 2 previous errors
+error: erroneous constant used
+  --> $DIR/issue-56164.rs:1:18
+   |
+LL | const fn foo() { (||{})() }
+   |                  ^^^^^^ referenced constant has errors
+   |
+   = note: `#[deny(const_err)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0015`.
diff --git a/src/test/ui/consts/issue-66693.rs b/src/test/ui/consts/issue-66693.rs
index 909bef7aefb..99d28eb773f 100644
--- a/src/test/ui/consts/issue-66693.rs
+++ b/src/test/ui/consts/issue-66693.rs
@@ -8,7 +8,10 @@ static _FOO: () = panic!(true);
 //~^ ERROR: argument to `panic!()` in a const context must have type `&str`
 
 const fn _foo() {
-    panic!(&1); //~ ERROR: argument to `panic!()` in a const context must have type `&str`
+    panic!(&1);
+    //~^ ERROR: argument to `panic!()` in a const context must have type `&str`
+    //~| ERROR: erroneous constant used [const_err]
+    //~| WARNING: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 }
 
 // ensure that conforming panics don't cause an error
diff --git a/src/test/ui/consts/issue-66693.stderr b/src/test/ui/consts/issue-66693.stderr
index 3530324bda2..b8257684983 100644
--- a/src/test/ui/consts/issue-66693.stderr
+++ b/src/test/ui/consts/issue-66693.stderr
@@ -22,5 +22,15 @@ LL |     panic!(&1);
    |
    = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 3 previous errors
+error: erroneous constant used
+  --> $DIR/issue-66693.rs:11:12
+   |
+LL |     panic!(&1);
+   |            ^^ referenced constant has errors
+   |
+   = note: `#[deny(const_err)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
+
+error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/consts/promotion.rs b/src/test/ui/consts/promotion.rs
index 580c6d62f10..e379e3aea13 100644
--- a/src/test/ui/consts/promotion.rs
+++ b/src/test/ui/consts/promotion.rs
@@ -4,11 +4,13 @@
 //[opt_with_overflow_checks]compile-flags: -C overflow-checks=on -O
 
 // build-pass
-#[allow(arithmetic_overflow)]
 
 const fn assert_static<T>(_: &'static T) {}
 
-const fn fail() -> i32 { 1/0 }
+#[allow(unconditional_panic)]
+const fn fail() -> i32 {
+    1/0
+}
 const C: i32 = {
     // Promoted that fails to evaluate in dead code -- this must work
     // (for backwards compatibility reasons).
diff --git a/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs b/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs
index 4fd5622b062..366ea7d3b3f 100644
--- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs
+++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs
@@ -2,7 +2,6 @@
 //~| NOTE ...which requires computing layout of `core::option::Option<<S as Mirror>::It>`...
 //~| NOTE ...which requires computing layout of `core::option::Option<S>`...
 //~| NOTE ...which again requires computing layout of `S`, completing the cycle
-//~| NOTE cycle used when computing layout of `core::option::Option<S>`
 
 // build-fail
 
@@ -15,5 +14,6 @@ impl<T: ?Sized> Mirror for T {
 struct S(Option<<S as Mirror>::It>);
 
 fn main() {
+    //~^ NOTE cycle used when elaborating drops for `main`
     let _s = S(None);
 }
diff --git a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr
index 6042379a918..5b675dc9f81 100644
--- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr
+++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr
@@ -3,7 +3,11 @@ error[E0391]: cycle detected when computing layout of `S`
    = note: ...which requires computing layout of `core::option::Option<<S as Mirror>::It>`...
    = note: ...which requires computing layout of `core::option::Option<S>`...
    = note: ...which again requires computing layout of `S`, completing the cycle
-   = note: cycle used when computing layout of `core::option::Option<S>`
+note: cycle used when elaborating drops for `main`
+  --> $DIR/issue-26548-recursion-via-normalize.rs:16:1
+   |
+LL | fn main() {
+   | ^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed
index acb0aa420ab..5786ed7b1d5 100644
--- a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed
+++ b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed
@@ -17,28 +17,18 @@ crate mod foo {
 use crate::foo::{bar::{baz::{}}};
 //~^ ERROR absolute paths must start with
 //~| WARN this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| WARN this is accepted in the current edition
 
 use crate::foo::{bar::{XX, baz::{}}};
 //~^ ERROR absolute paths must start with
 //~| WARN this is accepted in the current edition
 //~| ERROR absolute paths must start with
 //~| WARN this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| WARN this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| WARN this is accepted in the current edition
 
 use crate::foo::{bar::{baz::{}, baz1::{}}};
 //~^ ERROR absolute paths must start with
 //~| WARN this is accepted in the current edition
 //~| ERROR absolute paths must start with
 //~| WARN this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| WARN this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| WARN this is accepted in the current edition
 
 fn main() {
 }
diff --git a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs
index 4825528e97f..b7c86088c75 100644
--- a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs
+++ b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs
@@ -17,28 +17,18 @@ crate mod foo {
 use foo::{bar::{baz::{}}};
 //~^ ERROR absolute paths must start with
 //~| WARN this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| WARN this is accepted in the current edition
 
 use foo::{bar::{XX, baz::{}}};
 //~^ ERROR absolute paths must start with
 //~| WARN this is accepted in the current edition
 //~| ERROR absolute paths must start with
 //~| WARN this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| WARN this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| WARN this is accepted in the current edition
 
 use foo::{bar::{baz::{}, baz1::{}}};
 //~^ ERROR absolute paths must start with
 //~| WARN this is accepted in the current edition
 //~| ERROR absolute paths must start with
 //~| WARN this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| WARN this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| WARN this is accepted in the current edition
 
 fn main() {
 }
diff --git a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr
index 8a3113729b4..e47c320f78f 100644
--- a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr
+++ b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr
@@ -13,16 +13,7 @@ LL | #![deny(absolute_paths_not_starting_with_crate)]
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-empty-paths.rs:17:5
-   |
-LL | use foo::{bar::{baz::{}}};
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}}}`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-empty-paths.rs:23:5
+  --> $DIR/edition-lint-nested-empty-paths.rs:21:5
    |
 LL | use foo::{bar::{XX, baz::{}}};
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{XX, baz::{}}}`
@@ -31,7 +22,7 @@ LL | use foo::{bar::{XX, baz::{}}};
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-empty-paths.rs:23:5
+  --> $DIR/edition-lint-nested-empty-paths.rs:21:5
    |
 LL | use foo::{bar::{XX, baz::{}}};
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{XX, baz::{}}}`
@@ -40,43 +31,7 @@ LL | use foo::{bar::{XX, baz::{}}};
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-empty-paths.rs:23:5
-   |
-LL | use foo::{bar::{XX, baz::{}}};
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{XX, baz::{}}}`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-empty-paths.rs:23:5
-   |
-LL | use foo::{bar::{XX, baz::{}}};
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{XX, baz::{}}}`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-empty-paths.rs:33:5
-   |
-LL | use foo::{bar::{baz::{}, baz1::{}}};
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}, baz1::{}}}`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-empty-paths.rs:33:5
-   |
-LL | use foo::{bar::{baz::{}, baz1::{}}};
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}, baz1::{}}}`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-empty-paths.rs:33:5
+  --> $DIR/edition-lint-nested-empty-paths.rs:27:5
    |
 LL | use foo::{bar::{baz::{}, baz1::{}}};
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}, baz1::{}}}`
@@ -85,7 +40,7 @@ LL | use foo::{bar::{baz::{}, baz1::{}}};
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-empty-paths.rs:33:5
+  --> $DIR/edition-lint-nested-empty-paths.rs:27:5
    |
 LL | use foo::{bar::{baz::{}, baz1::{}}};
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}, baz1::{}}}`
@@ -93,5 +48,5 @@ LL | use foo::{bar::{baz::{}, baz1::{}}};
    = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
-error: aborting due to 10 previous errors
+error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/rust-2018/edition-lint-nested-paths.fixed b/src/test/ui/rust-2018/edition-lint-nested-paths.fixed
index 4eb1184cb6d..c4546f8c821 100644
--- a/src/test/ui/rust-2018/edition-lint-nested-paths.fixed
+++ b/src/test/ui/rust-2018/edition-lint-nested-paths.fixed
@@ -8,10 +8,6 @@ use crate::foo::{a, b};
 //~| this is accepted in the current edition
 //~| ERROR absolute paths must start with
 //~| this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| this is accepted in the current edition
 
 mod foo {
     crate fn a() {}
@@ -29,8 +25,6 @@ fn main() {
         //~| this is accepted in the current edition
         //~| ERROR absolute paths must start with
         //~| this is accepted in the current edition
-        //~| ERROR absolute paths must start with
-        //~| this is accepted in the current edition
         x::a();
         c();
     }
diff --git a/src/test/ui/rust-2018/edition-lint-nested-paths.rs b/src/test/ui/rust-2018/edition-lint-nested-paths.rs
index 2a358224d9b..a7e34e407a3 100644
--- a/src/test/ui/rust-2018/edition-lint-nested-paths.rs
+++ b/src/test/ui/rust-2018/edition-lint-nested-paths.rs
@@ -8,10 +8,6 @@ use foo::{a, b};
 //~| this is accepted in the current edition
 //~| ERROR absolute paths must start with
 //~| this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| this is accepted in the current edition
-//~| ERROR absolute paths must start with
-//~| this is accepted in the current edition
 
 mod foo {
     crate fn a() {}
@@ -29,8 +25,6 @@ fn main() {
         //~| this is accepted in the current edition
         //~| ERROR absolute paths must start with
         //~| this is accepted in the current edition
-        //~| ERROR absolute paths must start with
-        //~| this is accepted in the current edition
         x::a();
         c();
     }
diff --git a/src/test/ui/rust-2018/edition-lint-nested-paths.stderr b/src/test/ui/rust-2018/edition-lint-nested-paths.stderr
index 3d596022b0a..24b17f212eb 100644
--- a/src/test/ui/rust-2018/edition-lint-nested-paths.stderr
+++ b/src/test/ui/rust-2018/edition-lint-nested-paths.stderr
@@ -22,34 +22,7 @@ LL | use foo::{a, b};
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-paths.rs:6:5
-   |
-LL | use foo::{a, b};
-   |     ^^^^^^^^^^^ help: use `crate`: `crate::foo::{a, b}`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-paths.rs:6:5
-   |
-LL | use foo::{a, b};
-   |     ^^^^^^^^^^^ help: use `crate`: `crate::foo::{a, b}`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-paths.rs:27:13
-   |
-LL |         use foo::{self as x, c};
-   |             ^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{self as x, c}`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-paths.rs:27:13
+  --> $DIR/edition-lint-nested-paths.rs:23:13
    |
 LL |         use foo::{self as x, c};
    |             ^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{self as x, c}`
@@ -58,7 +31,7 @@ LL |         use foo::{self as x, c};
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-nested-paths.rs:27:13
+  --> $DIR/edition-lint-nested-paths.rs:23:13
    |
 LL |         use foo::{self as x, c};
    |             ^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{self as x, c}`
@@ -66,5 +39,5 @@ LL |         use foo::{self as x, c};
    = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
-error: aborting due to 7 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/rust-2018/edition-lint-paths.fixed b/src/test/ui/rust-2018/edition-lint-paths.fixed
index 46adf02a391..47f82c51dae 100644
--- a/src/test/ui/rust-2018/edition-lint-paths.fixed
+++ b/src/test/ui/rust-2018/edition-lint-paths.fixed
@@ -12,8 +12,6 @@ pub mod foo {
     use crate::bar::Bar;
     //~^ ERROR absolute
     //~| WARN this is accepted in the current edition
-    //~| ERROR absolute
-    //~| WARN this is accepted in the current edition
 
     use super::bar::Bar2;
     use crate::bar::Bar3;
@@ -42,8 +40,6 @@ pub mod foo {
 use crate::bar::Bar;
 //~^ ERROR absolute
 //~| WARN this is accepted in the current edition
-//~| ERROR absolute
-//~| WARN this is accepted in the current edition
 
 pub mod bar {
     use edition_lint_paths as foo;
@@ -61,8 +57,6 @@ mod baz {
 impl crate::foo::SomeTrait for u32 {}
 //~^ ERROR absolute
 //~| WARN this is accepted in the current edition
-//~| ERROR absolute
-//~| WARN this is accepted in the current edition
 
 fn main() {
     let x = crate::bar::Bar;
diff --git a/src/test/ui/rust-2018/edition-lint-paths.rs b/src/test/ui/rust-2018/edition-lint-paths.rs
index f70bf90d681..e278983da4a 100644
--- a/src/test/ui/rust-2018/edition-lint-paths.rs
+++ b/src/test/ui/rust-2018/edition-lint-paths.rs
@@ -12,8 +12,6 @@ pub mod foo {
     use bar::Bar;
     //~^ ERROR absolute
     //~| WARN this is accepted in the current edition
-    //~| ERROR absolute
-    //~| WARN this is accepted in the current edition
 
     use super::bar::Bar2;
     use crate::bar::Bar3;
@@ -42,8 +40,6 @@ pub mod foo {
 use bar::Bar;
 //~^ ERROR absolute
 //~| WARN this is accepted in the current edition
-//~| ERROR absolute
-//~| WARN this is accepted in the current edition
 
 pub mod bar {
     use edition_lint_paths as foo;
@@ -61,8 +57,6 @@ mod baz {
 impl ::foo::SomeTrait for u32 {}
 //~^ ERROR absolute
 //~| WARN this is accepted in the current edition
-//~| ERROR absolute
-//~| WARN this is accepted in the current edition
 
 fn main() {
     let x = ::bar::Bar;
diff --git a/src/test/ui/rust-2018/edition-lint-paths.stderr b/src/test/ui/rust-2018/edition-lint-paths.stderr
index 481c68e1033..1ded8cd3694 100644
--- a/src/test/ui/rust-2018/edition-lint-paths.stderr
+++ b/src/test/ui/rust-2018/edition-lint-paths.stderr
@@ -13,16 +13,7 @@ LL | #![deny(absolute_paths_not_starting_with_crate)]
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-paths.rs:12:9
-   |
-LL |     use bar::Bar;
-   |         ^^^^^^^^ help: use `crate`: `crate::bar::Bar`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-paths.rs:21:9
+  --> $DIR/edition-lint-paths.rs:19:9
    |
 LL |     use bar;
    |         ^^^ help: use `crate`: `crate::bar`
@@ -31,7 +22,7 @@ LL |     use bar;
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-paths.rs:27:9
+  --> $DIR/edition-lint-paths.rs:25:9
    |
 LL |     use {main, Bar as SomethingElse};
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::{main, Bar as SomethingElse}`
@@ -40,7 +31,7 @@ LL |     use {main, Bar as SomethingElse};
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-paths.rs:27:9
+  --> $DIR/edition-lint-paths.rs:25:9
    |
 LL |     use {main, Bar as SomethingElse};
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::{main, Bar as SomethingElse}`
@@ -49,7 +40,7 @@ LL |     use {main, Bar as SomethingElse};
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-paths.rs:27:9
+  --> $DIR/edition-lint-paths.rs:25:9
    |
 LL |     use {main, Bar as SomethingElse};
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::{main, Bar as SomethingElse}`
@@ -58,7 +49,7 @@ LL |     use {main, Bar as SomethingElse};
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-paths.rs:42:5
+  --> $DIR/edition-lint-paths.rs:40:5
    |
 LL | use bar::Bar;
    |     ^^^^^^^^ help: use `crate`: `crate::bar::Bar`
@@ -67,16 +58,7 @@ LL | use bar::Bar;
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-paths.rs:42:5
-   |
-LL | use bar::Bar;
-   |     ^^^^^^^^ help: use `crate`: `crate::bar::Bar`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-paths.rs:56:9
+  --> $DIR/edition-lint-paths.rs:52:9
    |
 LL |     use *;
    |         ^ help: use `crate`: `crate::*`
@@ -85,16 +67,7 @@ LL |     use *;
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-paths.rs:61:6
-   |
-LL | impl ::foo::SomeTrait for u32 {}
-   |      ^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::SomeTrait`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-paths.rs:61:6
+  --> $DIR/edition-lint-paths.rs:57:6
    |
 LL | impl ::foo::SomeTrait for u32 {}
    |      ^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::SomeTrait`
@@ -103,7 +76,7 @@ LL | impl ::foo::SomeTrait for u32 {}
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
 error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/edition-lint-paths.rs:68:13
+  --> $DIR/edition-lint-paths.rs:62:13
    |
 LL |     let x = ::bar::Bar;
    |             ^^^^^^^^^^ help: use `crate`: `crate::bar::Bar`
@@ -111,5 +84,5 @@ LL |     let x = ::bar::Bar;
    = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
-error: aborting due to 12 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/src/test/ui/rust-2018/extern-crate-rename.fixed b/src/test/ui/rust-2018/extern-crate-rename.fixed
index 05b881a9b08..ea832ef3e7d 100644
--- a/src/test/ui/rust-2018/extern-crate-rename.fixed
+++ b/src/test/ui/rust-2018/extern-crate-rename.fixed
@@ -12,8 +12,6 @@ extern crate edition_lint_paths as my_crate;
 use crate::my_crate::foo;
 //~^ ERROR absolute paths must start
 //~| WARNING this is accepted in the current edition
-//~| ERROR absolute paths must start
-//~| WARNING this is accepted in the current edition
 
 fn main() {
     foo();
diff --git a/src/test/ui/rust-2018/extern-crate-rename.rs b/src/test/ui/rust-2018/extern-crate-rename.rs
index 6e327be193b..b1f617dd884 100644
--- a/src/test/ui/rust-2018/extern-crate-rename.rs
+++ b/src/test/ui/rust-2018/extern-crate-rename.rs
@@ -12,8 +12,6 @@ extern crate edition_lint_paths as my_crate;
 use my_crate::foo;
 //~^ ERROR absolute paths must start
 //~| WARNING this is accepted in the current edition
-//~| ERROR absolute paths must start
-//~| WARNING this is accepted in the current edition
 
 fn main() {
     foo();
diff --git a/src/test/ui/rust-2018/extern-crate-rename.stderr b/src/test/ui/rust-2018/extern-crate-rename.stderr
index f2f379ca396..4bccbc51223 100644
--- a/src/test/ui/rust-2018/extern-crate-rename.stderr
+++ b/src/test/ui/rust-2018/extern-crate-rename.stderr
@@ -12,14 +12,5 @@ LL | #![deny(absolute_paths_not_starting_with_crate)]
    = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/extern-crate-rename.rs:12:5
-   |
-LL | use my_crate::foo;
-   |     ^^^^^^^^^^^^^ help: use `crate`: `crate::my_crate::foo`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
diff --git a/src/test/ui/rust-2018/extern-crate-submod.fixed b/src/test/ui/rust-2018/extern-crate-submod.fixed
index fdbd893abed..9b0b0dd8ee1 100644
--- a/src/test/ui/rust-2018/extern-crate-submod.fixed
+++ b/src/test/ui/rust-2018/extern-crate-submod.fixed
@@ -19,9 +19,6 @@ mod m {
 use crate::m::edition_lint_paths::foo;
 //~^ ERROR absolute paths must start
 //~| WARNING this is accepted in the current edition
-//~| ERROR absolute paths must start
-//~| WARNING this is accepted in the current edition
-
 
 fn main() {
     foo();
diff --git a/src/test/ui/rust-2018/extern-crate-submod.rs b/src/test/ui/rust-2018/extern-crate-submod.rs
index c2b915849f0..dfce9128c51 100644
--- a/src/test/ui/rust-2018/extern-crate-submod.rs
+++ b/src/test/ui/rust-2018/extern-crate-submod.rs
@@ -19,9 +19,6 @@ mod m {
 use m::edition_lint_paths::foo;
 //~^ ERROR absolute paths must start
 //~| WARNING this is accepted in the current edition
-//~| ERROR absolute paths must start
-//~| WARNING this is accepted in the current edition
-
 
 fn main() {
     foo();
diff --git a/src/test/ui/rust-2018/extern-crate-submod.stderr b/src/test/ui/rust-2018/extern-crate-submod.stderr
index c4c3168bc97..3c75319aeda 100644
--- a/src/test/ui/rust-2018/extern-crate-submod.stderr
+++ b/src/test/ui/rust-2018/extern-crate-submod.stderr
@@ -12,14 +12,5 @@ LL | #![deny(absolute_paths_not_starting_with_crate)]
    = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
    = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
 
-error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
-  --> $DIR/extern-crate-submod.rs:19:5
-   |
-LL | use m::edition_lint_paths::foo;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::m::edition_lint_paths::foo`
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
-   = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130>
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
diff --git a/src/test/ui/typeck/issue-90319.rs b/src/test/ui/typeck/issue-90319.rs
new file mode 100644
index 00000000000..57e6ac7cf34
--- /dev/null
+++ b/src/test/ui/typeck/issue-90319.rs
@@ -0,0 +1,17 @@
+struct Wrapper<T>(T);
+
+trait Trait {
+    fn method(&self) {}
+}
+
+impl<'a, T> Trait for Wrapper<&'a T> where Wrapper<T>: Trait {}
+
+fn get<T>() -> T {
+    unimplemented!()
+}
+
+fn main() {
+    let thing = get::<Thing>();//~ERROR cannot find type `Thing` in this scope [E0412]
+    let wrapper = Wrapper(thing);
+    Trait::method(&wrapper);
+}
diff --git a/src/test/ui/typeck/issue-90319.stderr b/src/test/ui/typeck/issue-90319.stderr
new file mode 100644
index 00000000000..61549dd701e
--- /dev/null
+++ b/src/test/ui/typeck/issue-90319.stderr
@@ -0,0 +1,9 @@
+error[E0412]: cannot find type `Thing` in this scope
+  --> $DIR/issue-90319.rs:14:23
+   |
+LL |     let thing = get::<Thing>();
+   |                       ^^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml
index 68877efc9e1..b6f70a7f183 100644
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -18,7 +18,7 @@ body:
     id: reproducer
     attributes:
       label: Reproducer
-      description: Please provide the code and steps to repoduce the bug
+      description: Please provide the code and steps to reproduce the bug
       value: |
         I tried this code:
 
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 2bc393d6042..88f71931d92 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -3069,6 +3069,7 @@ Released 2018-09-13
 [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
+[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
 [`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
 [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
 [`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
@@ -3366,6 +3367,7 @@ Released 2018-09-13
 [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
 [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
 [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
+[`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap
 [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
 [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
 [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
@@ -3506,6 +3508,7 @@ Released 2018-09-13
 [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 [`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
 [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
+[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
 [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_enum_constructor.rs b/src/tools/clippy/clippy_lints/src/casts/cast_enum_constructor.rs
new file mode 100644
index 00000000000..1973692e105
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_enum_constructor.rs
@@ -0,0 +1,21 @@
+use clippy_utils::diagnostics::span_lint;
+use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty};
+
+use super::CAST_ENUM_CONSTRUCTOR;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>) {
+    if matches!(cast_from.kind(), ty::FnDef(..))
+        && let ExprKind::Path(path) = &cast_expr.kind
+        && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = cx.qpath_res(path, cast_expr.hir_id)
+    {
+        span_lint(
+            cx,
+            CAST_ENUM_CONSTRUCTOR,
+            expr.span,
+            "cast of an enum tuple constructor to an integer",
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index 6a0eabd089d..be59145afa0 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -1,3 +1,4 @@
+mod cast_enum_constructor;
 mod cast_lossless;
 mod cast_possible_truncation;
 mod cast_possible_wrap;
@@ -454,6 +455,24 @@ declare_clippy_lint! {
     "casting using `as` between raw pointers to slices of types with different sizes"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for casts from an enum tuple constructor to an integer.
+    ///
+    /// ### Why is this bad?
+    /// The cast is easily confused with casting a c-like enum value to an integer.
+    ///
+    /// ### Example
+    /// ```rust
+    /// enum E { X(i32) };
+    /// let _ = E::X as usize;
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub CAST_ENUM_CONSTRUCTOR,
+    suspicious,
+    "casts from an enum tuple constructor to an integer"
+}
+
 pub struct Casts {
     msrv: Option<RustcVersion>,
 }
@@ -481,6 +500,7 @@ impl_lint_pass!(Casts => [
     CHAR_LIT_AS_U8,
     PTR_AS_PTR,
     CAST_ENUM_TRUNCATION,
+    CAST_ENUM_CONSTRUCTOR
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -518,6 +538,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
                     cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
                 }
                 cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
+                cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
             }
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
index f03f34e5a4b..eae2723a7da 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
@@ -42,7 +42,7 @@ declare_clippy_lint! {
     ///
     /// Should be written:
     ///
-    /// ```rust.ignore
+    /// ```rust,ignore
     /// if x && y {
     ///     …
     /// }
@@ -76,7 +76,7 @@ declare_clippy_lint! {
     ///
     /// Should be written:
     ///
-    /// ```rust.ignore
+    /// ```rust,ignore
     /// if x {
     ///     …
     /// } else if y {
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index 16173580fd4..703aa458f44 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -637,12 +637,6 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
                 loop {
                     match parser.parse_item(ForceCollect::No) {
                         Ok(Some(item)) => match &item.kind {
-                            // Tests with one of these items are ignored
-                            ItemKind::Static(..)
-                            | ItemKind::Const(..)
-                            | ItemKind::ExternCrate(..)
-                            | ItemKind::ForeignMod(..) => return false,
-                            // We found a main function ...
                             ItemKind::Fn(box Fn {
                                 sig, body: Some(block), ..
                             }) if item.ident.name == sym::main => {
@@ -661,8 +655,13 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
                                     return false;
                                 }
                             },
-                            // Another function was found; this case is ignored too
-                            ItemKind::Fn(..) => return false,
+                            // Tests with one of these items are ignored
+                            ItemKind::Static(..)
+                            | ItemKind::Const(..)
+                            | ItemKind::ExternCrate(..)
+                            | ItemKind::ForeignMod(..)
+                            // Another function was found; this case is ignored
+                            | ItemKind::Fn(..) => return false,
                             _ => {},
                         },
                         Ok(None) => break,
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs
index 23bca5a0eab..132a4662676 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs
@@ -23,6 +23,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
     LintId::of(booleans::LOGIC_BUG),
     LintId::of(booleans::NONMINIMAL_BOOL),
+    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
     LintId::of(casts::CAST_ENUM_TRUNCATION),
     LintId::of(casts::CAST_REF_TO_MUT),
     LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
@@ -166,7 +167,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(methods::ITER_NTH_ZERO),
     LintId::of(methods::ITER_OVEREAGER_CLONED),
     LintId::of(methods::ITER_SKIP_NEXT),
-    LintId::of(methods::ITER_WITH_DRAIN),
     LintId::of(methods::MANUAL_FILTER_MAP),
     LintId::of(methods::MANUAL_FIND_MAP),
     LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
@@ -182,6 +182,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(methods::OPTION_FILTER_MAP),
     LintId::of(methods::OPTION_MAP_OR_NONE),
     LintId::of(methods::OR_FUN_CALL),
+    LintId::of(methods::OR_THEN_UNWRAP),
     LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
     LintId::of(methods::SEARCH_IS_SOME),
     LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
@@ -290,7 +291,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
     LintId::of(transmute::WRONG_TRANSMUTE),
     LintId::of(transmuting_null::TRANSMUTING_NULL),
-    LintId::of(try_err::TRY_ERR),
     LintId::of(types::BORROWED_BOX),
     LintId::of(types::BOX_COLLECTION),
     LintId::of(types::REDUNDANT_ALLOCATION),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
index 68d6c6ce5f7..a2ce69065f9 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
@@ -47,6 +47,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::OPTION_AS_REF_DEREF),
     LintId::of(methods::OPTION_FILTER_MAP),
+    LintId::of(methods::OR_THEN_UNWRAP),
     LintId::of(methods::SEARCH_IS_SOME),
     LintId::of(methods::SKIP_WHILE_NEXT),
     LintId::of(methods::UNNECESSARY_FILTER_MAP),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs
index 1a45763a869..21f1ef562b5 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs
@@ -70,6 +70,7 @@ store.register_lints(&[
     cargo::REDUNDANT_FEATURE_NAMES,
     cargo::WILDCARD_DEPENDENCIES,
     case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+    casts::CAST_ENUM_CONSTRUCTOR,
     casts::CAST_ENUM_TRUNCATION,
     casts::CAST_LOSSLESS,
     casts::CAST_POSSIBLE_TRUNCATION,
@@ -319,6 +320,7 @@ store.register_lints(&[
     methods::OPTION_FILTER_MAP,
     methods::OPTION_MAP_OR_NONE,
     methods::OR_FUN_CALL,
+    methods::OR_THEN_UNWRAP,
     methods::RESULT_MAP_OR_INTO_OPTION,
     methods::SEARCH_IS_SOME,
     methods::SHOULD_IMPLEMENT_TRAIT,
@@ -332,6 +334,7 @@ store.register_lints(&[
     methods::UNNECESSARY_FILTER_MAP,
     methods::UNNECESSARY_FIND_MAP,
     methods::UNNECESSARY_FOLD,
+    methods::UNNECESSARY_JOIN,
     methods::UNNECESSARY_LAZY_EVALUATIONS,
     methods::UNNECESSARY_TO_OWNED,
     methods::UNWRAP_OR_ELSE_DEFAULT,
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs
index 8d4dde42bbe..c2fc67afba5 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs
@@ -13,6 +13,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
     LintId::of(future_not_send::FUTURE_NOT_SEND),
     LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
     LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
+    LintId::of(methods::ITER_WITH_DRAIN),
     LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
     LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
     LintId::of(mutex_atomic::MUTEX_ATOMIC),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
index 00d30513181..eb6534cb8ca 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
@@ -63,6 +63,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(methods::IMPLICIT_CLONE),
     LintId::of(methods::INEFFICIENT_TO_STRING),
     LintId::of(methods::MAP_UNWRAP_OR),
+    LintId::of(methods::UNNECESSARY_JOIN),
     LintId::of(misc::FLOAT_CMP),
     LintId::of(misc::USED_UNDERSCORE_BINDING),
     LintId::of(mut_mut::MUT_MUT),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs
index 6e9c0ee33a1..f2f5c988d8f 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs
@@ -16,7 +16,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
     LintId::of(methods::EXTEND_WITH_DRAIN),
     LintId::of(methods::ITER_NTH),
     LintId::of(methods::ITER_OVEREAGER_CLONED),
-    LintId::of(methods::ITER_WITH_DRAIN),
     LintId::of(methods::MANUAL_STR_REPEAT),
     LintId::of(methods::OR_FUN_CALL),
     LintId::of(methods::SINGLE_CHAR_PATTERN),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
index 4e30fc38197..6ab139b2fb6 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
@@ -62,6 +62,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(strings::STRING_SLICE),
     LintId::of(strings::STRING_TO_STRING),
     LintId::of(strings::STR_TO_STRING),
+    LintId::of(try_err::TRY_ERR),
     LintId::of(types::RC_BUFFER),
     LintId::of(types::RC_MUTEX),
     LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_style.rs b/src/tools/clippy/clippy_lints/src/lib.register_style.rs
index 05211476ff2..dcf399cf956 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_style.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_style.rs
@@ -105,7 +105,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
     LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
     LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
-    LintId::of(try_err::TRY_ERR),
     LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
     LintId::of(unused_unit::UNUSED_UNIT),
     LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
index 465baa82581..fa3a88e1368 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
@@ -7,6 +7,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
+    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
     LintId::of(casts::CAST_ENUM_TRUNCATION),
     LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
     LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 504235d0d1e..f2a07999144 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -23,6 +23,7 @@
 
 // FIXME: switch to something more ergonomic here, once available.
 // (Currently there is no way to opt into sysroot crates without `extern crate`.)
+extern crate rustc_arena;
 extern crate rustc_ast;
 extern crate rustc_ast_pretty;
 extern crate rustc_attr;
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
index d11dda57e6f..b8591fe0db0 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
@@ -1,19 +1,66 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash};
-use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdSet, Pat, PatKind};
+use core::cmp::Ordering;
+use core::iter;
+use core::slice;
+use rustc_arena::DroplessArena;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdSet, Pat, PatKind, RangeEnd};
 use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::Symbol;
 use std::collections::hash_map::Entry;
 
 use super::MATCH_SAME_ARMS;
 
-pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
+#[allow(clippy::too_many_lines)]
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
     let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
         let mut h = SpanlessHash::new(cx);
         h.hash_expr(arm.body);
         h.finish()
     };
 
+    let arena = DroplessArena::default();
+    let normalized_pats: Vec<_> = arms
+        .iter()
+        .map(|a| NormalizedPat::from_pat(cx, &arena, a.pat))
+        .collect();
+
+    // The furthast forwards a pattern can move without semantic changes
+    let forwards_blocking_idxs: Vec<_> = normalized_pats
+        .iter()
+        .enumerate()
+        .map(|(i, pat)| {
+            normalized_pats[i + 1..]
+                .iter()
+                .enumerate()
+                .find_map(|(j, other)| pat.has_overlapping_values(other).then(|| i + 1 + j))
+                .unwrap_or(normalized_pats.len())
+        })
+        .collect();
+
+    // The furthast backwards a pattern can move without semantic changes
+    let backwards_blocking_idxs: Vec<_> = normalized_pats
+        .iter()
+        .enumerate()
+        .map(|(i, pat)| {
+            normalized_pats[..i]
+                .iter()
+                .enumerate()
+                .rev()
+                .zip(forwards_blocking_idxs[..i].iter().copied().rev())
+                .skip_while(|&(_, forward_block)| forward_block > i)
+                .find_map(|((j, other), forward_block)| {
+                    (forward_block == i || pat.has_overlapping_values(other)).then(|| j)
+                })
+                .unwrap_or(0)
+        })
+        .collect();
+
     let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
         let min_index = usize::min(lindex, rindex);
         let max_index = usize::max(lindex, rindex);
@@ -42,53 +89,316 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
             }
         };
         // Arms with a guard are ignored, those can’t always be merged together
-        // This is also the case for arms in-between each there is an arm with a guard
-        (min_index..=max_index).all(|index| arms[index].guard.is_none())
-            && SpanlessEq::new(cx)
-                .expr_fallback(eq_fallback)
-                .eq_expr(lhs.body, rhs.body)
-            // these checks could be removed to allow unused bindings
-            && bindings_eq(lhs.pat, local_map.keys().copied().collect())
-            && bindings_eq(rhs.pat, local_map.values().copied().collect())
+        // If both arms overlap with an arm in between then these can't be merged either.
+        !(backwards_blocking_idxs[max_index] > min_index && forwards_blocking_idxs[min_index] < max_index)
+                && lhs.guard.is_none()
+                && rhs.guard.is_none()
+                && SpanlessEq::new(cx)
+                    .expr_fallback(eq_fallback)
+                    .eq_expr(lhs.body, rhs.body)
+                // these checks could be removed to allow unused bindings
+                && bindings_eq(lhs.pat, local_map.keys().copied().collect())
+                && bindings_eq(rhs.pat, local_map.values().copied().collect())
     };
 
     let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
-    for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
-        span_lint_and_then(
-            cx,
-            MATCH_SAME_ARMS,
-            j.body.span,
-            "this `match` has identical arm bodies",
-            |diag| {
-                diag.span_note(i.body.span, "same as this");
-
-                // Note: this does not use `span_suggestion` on purpose:
-                // there is no clean way
-                // to remove the other arm. Building a span and suggest to replace it to ""
-                // makes an even more confusing error message. Also in order not to make up a
-                // span for the whole pattern, the suggestion is only shown when there is only
-                // one pattern. The user should know about `|` if they are already using it…
-
-                let lhs = snippet(cx, i.pat.span, "<pat1>");
-                let rhs = snippet(cx, j.pat.span, "<pat2>");
-
-                if let PatKind::Wild = j.pat.kind {
-                    // if the last arm is _, then i could be integrated into _
-                    // note that i.pat cannot be _, because that would mean that we're
-                    // hiding all the subsequent arms, and rust won't compile
-                    diag.span_note(
-                        i.body.span,
-                        &format!(
-                            "`{}` has the same arm body as the `_` wildcard, consider removing it",
-                            lhs
-                        ),
-                    );
+    for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
+        if matches!(arm2.pat.kind, PatKind::Wild) {
+            span_lint_and_then(
+                cx,
+                MATCH_SAME_ARMS,
+                arm1.span,
+                "this match arm has an identical body to the `_` wildcard arm",
+                |diag| {
+                    diag.span_suggestion(
+                        arm1.span,
+                        "try removing the arm",
+                        String::new(),
+                        Applicability::MaybeIncorrect,
+                    )
+                    .help("or try changing either arm body")
+                    .span_note(arm2.span, "`_` wildcard arm here");
+                },
+            );
+        } else {
+            let back_block = backwards_blocking_idxs[j];
+            let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) {
+                (arm1, arm2)
+            } else {
+                (arm2, arm1)
+            };
+
+            span_lint_and_then(
+                cx,
+                MATCH_SAME_ARMS,
+                keep_arm.span,
+                "this match arm has an identical body to another arm",
+                |diag| {
+                    let move_pat_snip = snippet(cx, move_arm.pat.span, "<pat2>");
+                    let keep_pat_snip = snippet(cx, keep_arm.pat.span, "<pat1>");
+
+                    diag.span_suggestion(
+                        keep_arm.pat.span,
+                        "try merging the arm patterns",
+                        format!("{} | {}", keep_pat_snip, move_pat_snip),
+                        Applicability::MaybeIncorrect,
+                    )
+                    .help("or try changing either arm body")
+                    .span_note(move_arm.span, "other arm here");
+                },
+            );
+        }
+    }
+}
+
+#[derive(Clone, Copy)]
+enum NormalizedPat<'a> {
+    Wild,
+    Struct(Option<DefId>, &'a [(Symbol, Self)]),
+    Tuple(Option<DefId>, &'a [Self]),
+    Or(&'a [Self]),
+    Path(Option<DefId>),
+    LitStr(Symbol),
+    LitBytes(&'a [u8]),
+    LitInt(u128),
+    LitBool(bool),
+    Range(PatRange),
+    /// A slice pattern. If the second value is `None`, then this matches an exact size. Otherwise
+    /// the first value contains everything before the `..` wildcard pattern, and the second value
+    /// contains everything afterwards. Note that either side, or both sides, may contain zero
+    /// patterns.
+    Slice(&'a [Self], Option<&'a [Self]>),
+}
+
+#[derive(Clone, Copy)]
+struct PatRange {
+    start: u128,
+    end: u128,
+    bounds: RangeEnd,
+}
+impl PatRange {
+    fn contains(&self, x: u128) -> bool {
+        x >= self.start
+            && match self.bounds {
+                RangeEnd::Included => x <= self.end,
+                RangeEnd::Excluded => x < self.end,
+            }
+    }
+
+    fn overlaps(&self, other: &Self) -> bool {
+        // Note: Empty ranges are impossible, so this is correct even though it would return true if an
+        // empty exclusive range were to reside within an inclusive range.
+        (match self.bounds {
+            RangeEnd::Included => self.end >= other.start,
+            RangeEnd::Excluded => self.end > other.start,
+        } && match other.bounds {
+            RangeEnd::Included => self.start <= other.end,
+            RangeEnd::Excluded => self.start < other.end,
+        })
+    }
+}
+
+/// Iterates over the pairs of fields with matching names.
+fn iter_matching_struct_fields<'a>(
+    left: &'a [(Symbol, NormalizedPat<'a>)],
+    right: &'a [(Symbol, NormalizedPat<'a>)],
+) -> impl Iterator<Item = (&'a NormalizedPat<'a>, &'a NormalizedPat<'a>)> + 'a {
+    struct Iter<'a>(
+        slice::Iter<'a, (Symbol, NormalizedPat<'a>)>,
+        slice::Iter<'a, (Symbol, NormalizedPat<'a>)>,
+    );
+    impl<'a> Iterator for Iter<'a> {
+        type Item = (&'a NormalizedPat<'a>, &'a NormalizedPat<'a>);
+        fn next(&mut self) -> Option<Self::Item> {
+            // Note: all the fields in each slice are sorted by symbol value.
+            let mut left = self.0.next()?;
+            let mut right = self.1.next()?;
+            loop {
+                match left.0.cmp(&right.0) {
+                    Ordering::Equal => return Some((&left.1, &right.1)),
+                    Ordering::Less => left = self.0.next()?,
+                    Ordering::Greater => right = self.1.next()?,
+                }
+            }
+        }
+    }
+    Iter(left.iter(), right.iter())
+}
+
+#[allow(clippy::similar_names)]
+impl<'a> NormalizedPat<'a> {
+    #[allow(clippy::too_many_lines)]
+    fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self {
+        match pat.kind {
+            PatKind::Wild | PatKind::Binding(.., None) => Self::Wild,
+            PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Ref(pat, _) => {
+                Self::from_pat(cx, arena, pat)
+            },
+            PatKind::Struct(ref path, fields, _) => {
+                let fields =
+                    arena.alloc_from_iter(fields.iter().map(|f| (f.ident.name, Self::from_pat(cx, arena, f.pat))));
+                fields.sort_by_key(|&(name, _)| name);
+                Self::Struct(cx.qpath_res(path, pat.hir_id).opt_def_id(), fields)
+            },
+            PatKind::TupleStruct(ref path, pats, wild_idx) => {
+                let adt = match cx.typeck_results().pat_ty(pat).ty_adt_def() {
+                    Some(x) => x,
+                    None => return Self::Wild,
+                };
+                let (var_id, variant) = if adt.is_enum() {
+                    match cx.qpath_res(path, pat.hir_id).opt_def_id() {
+                        Some(x) => (Some(x), adt.variant_with_ctor_id(x)),
+                        None => return Self::Wild,
+                    }
                 } else {
-                    diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
-                        .help("...or consider changing the match arm bodies");
+                    (None, adt.non_enum_variant())
+                };
+                let (front, back) = match wild_idx {
+                    Some(i) => pats.split_at(i),
+                    None => (pats, [].as_slice()),
+                };
+                let pats = arena.alloc_from_iter(
+                    front
+                        .iter()
+                        .map(|pat| Self::from_pat(cx, arena, pat))
+                        .chain(iter::repeat_with(|| Self::Wild).take(variant.fields.len() - pats.len()))
+                        .chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))),
+                );
+                Self::Tuple(var_id, pats)
+            },
+            PatKind::Or(pats) => Self::Or(arena.alloc_from_iter(pats.iter().map(|pat| Self::from_pat(cx, arena, pat)))),
+            PatKind::Path(ref path) => Self::Path(cx.qpath_res(path, pat.hir_id).opt_def_id()),
+            PatKind::Tuple(pats, wild_idx) => {
+                let field_count = match cx.typeck_results().pat_ty(pat).kind() {
+                    ty::Tuple(subs) => subs.len(),
+                    _ => return Self::Wild,
+                };
+                let (front, back) = match wild_idx {
+                    Some(i) => pats.split_at(i),
+                    None => (pats, [].as_slice()),
+                };
+                let pats = arena.alloc_from_iter(
+                    front
+                        .iter()
+                        .map(|pat| Self::from_pat(cx, arena, pat))
+                        .chain(iter::repeat_with(|| Self::Wild).take(field_count - pats.len()))
+                        .chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))),
+                );
+                Self::Tuple(None, pats)
+            },
+            PatKind::Lit(e) => match &e.kind {
+                // TODO: Handle negative integers. They're currently treated as a wild match.
+                ExprKind::Lit(lit) => match lit.node {
+                    LitKind::Str(sym, _) => Self::LitStr(sym),
+                    LitKind::ByteStr(ref bytes) => Self::LitBytes(&**bytes),
+                    LitKind::Byte(val) => Self::LitInt(val.into()),
+                    LitKind::Char(val) => Self::LitInt(val.into()),
+                    LitKind::Int(val, _) => Self::LitInt(val),
+                    LitKind::Bool(val) => Self::LitBool(val),
+                    LitKind::Float(..) | LitKind::Err(_) => Self::Wild,
+                },
+                _ => Self::Wild,
+            },
+            PatKind::Range(start, end, bounds) => {
+                // TODO: Handle negative integers. They're currently treated as a wild match.
+                let start = match start {
+                    None => 0,
+                    Some(e) => match &e.kind {
+                        ExprKind::Lit(lit) => match lit.node {
+                            LitKind::Int(val, _) => val,
+                            LitKind::Char(val) => val.into(),
+                            LitKind::Byte(val) => val.into(),
+                            _ => return Self::Wild,
+                        },
+                        _ => return Self::Wild,
+                    },
+                };
+                let (end, bounds) = match end {
+                    None => (u128::MAX, RangeEnd::Included),
+                    Some(e) => match &e.kind {
+                        ExprKind::Lit(lit) => match lit.node {
+                            LitKind::Int(val, _) => (val, bounds),
+                            LitKind::Char(val) => (val.into(), bounds),
+                            LitKind::Byte(val) => (val.into(), bounds),
+                            _ => return Self::Wild,
+                        },
+                        _ => return Self::Wild,
+                    },
+                };
+                Self::Range(PatRange { start, end, bounds })
+            },
+            PatKind::Slice(front, wild_pat, back) => Self::Slice(
+                arena.alloc_from_iter(front.iter().map(|pat| Self::from_pat(cx, arena, pat))),
+                wild_pat.map(|_| &*arena.alloc_from_iter(back.iter().map(|pat| Self::from_pat(cx, arena, pat)))),
+            ),
+        }
+    }
+
+    /// Checks if two patterns overlap in the values they can match assuming they are for the same
+    /// type.
+    fn has_overlapping_values(&self, other: &Self) -> bool {
+        match (*self, *other) {
+            (Self::Wild, _) | (_, Self::Wild) => true,
+            (Self::Or(pats), ref other) | (ref other, Self::Or(pats)) => {
+                pats.iter().any(|pat| pat.has_overlapping_values(other))
+            },
+            (Self::Struct(lpath, lfields), Self::Struct(rpath, rfields)) => {
+                if lpath != rpath {
+                    return false;
+                }
+                iter_matching_struct_fields(lfields, rfields).all(|(lpat, rpat)| lpat.has_overlapping_values(rpat))
+            },
+            (Self::Tuple(lpath, lpats), Self::Tuple(rpath, rpats)) => {
+                if lpath != rpath {
+                    return false;
                 }
+                lpats
+                    .iter()
+                    .zip(rpats.iter())
+                    .all(|(lpat, rpat)| lpat.has_overlapping_values(rpat))
+            },
+            (Self::Path(x), Self::Path(y)) => x == y,
+            (Self::LitStr(x), Self::LitStr(y)) => x == y,
+            (Self::LitBytes(x), Self::LitBytes(y)) => x == y,
+            (Self::LitInt(x), Self::LitInt(y)) => x == y,
+            (Self::LitBool(x), Self::LitBool(y)) => x == y,
+            (Self::Range(ref x), Self::Range(ref y)) => x.overlaps(y),
+            (Self::Range(ref range), Self::LitInt(x)) | (Self::LitInt(x), Self::Range(ref range)) => range.contains(x),
+            (Self::Slice(lpats, None), Self::Slice(rpats, None)) => {
+                lpats.len() == rpats.len() && lpats.iter().zip(rpats.iter()).all(|(x, y)| x.has_overlapping_values(y))
             },
-        );
+            (Self::Slice(pats, None), Self::Slice(front, Some(back)))
+            | (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => {
+                // Here `pats` is an exact size match. If the combined lengths of `front` and `back` are greater
+                // then the minium length required will be greater than the length of `pats`.
+                if pats.len() < front.len() + back.len() {
+                    return false;
+                }
+                pats[..front.len()]
+                    .iter()
+                    .zip(front.iter())
+                    .chain(pats[pats.len() - back.len()..].iter().zip(back.iter()))
+                    .all(|(x, y)| x.has_overlapping_values(y))
+            },
+            (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => lfront
+                .iter()
+                .zip(rfront.iter())
+                .chain(lback.iter().rev().zip(rback.iter().rev()))
+                .all(|(x, y)| x.has_overlapping_values(y)),
+
+            // Enums can mix unit variants with tuple/struct variants. These can never overlap.
+            (Self::Path(_), Self::Tuple(..) | Self::Struct(..))
+            | (Self::Tuple(..) | Self::Struct(..), Self::Path(_)) => false,
+
+            // Tuples can be matched like a struct.
+            (Self::Tuple(x, _), Self::Struct(y, _)) | (Self::Struct(x, _), Self::Tuple(y, _)) => {
+                // TODO: check fields here.
+                x == y
+            },
+
+            // TODO: Lit* with Path, Range with Path, LitBytes with Slice
+            _ => true,
+        }
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
index e1212c31cfb..f447940ea3b 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
@@ -1,83 +1,73 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_sugg_for_edges;
 use clippy_utils::is_trait_method;
-use clippy_utils::source::snippet;
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::symbol::sym;
+use rustc_span::{symbol::sym, Span};
 
 use super::MAP_FLATTEN;
 
 /// lint use of `map().flatten()` for `Iterators` and 'Options'
-pub(super) fn check<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx hir::Expr<'_>,
-    recv: &'tcx hir::Expr<'_>,
-    map_arg: &'tcx hir::Expr<'_>,
-) {
-    // lint if caller of `.map().flatten()` is an Iterator
-    if is_trait_method(cx, expr, sym::Iterator) {
-        let map_closure_ty = cx.typeck_results().expr_ty(map_arg);
-        let is_map_to_option = match map_closure_ty.kind() {
-            ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
-                let map_closure_sig = match map_closure_ty.kind() {
-                    ty::Closure(_, substs) => substs.as_closure().sig(),
-                    _ => map_closure_ty.fn_sig(cx.tcx),
-                };
-                let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output());
-                is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option)
-            },
-            _ => false,
-        };
-
-        let method_to_use = if is_map_to_option {
-            // `(...).map(...)` has type `impl Iterator<Item=Option<...>>
-            "filter_map"
-        } else {
-            // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
-            "flat_map"
-        };
-        let func_snippet = snippet(cx, map_arg.span, "..");
-        let hint = format!(".{0}({1})", method_to_use, func_snippet);
-        span_lint_and_sugg(
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) {
+    if let Some((caller_ty_name, method_to_use)) = try_get_caller_ty_name_and_method_name(cx, expr, recv, map_arg) {
+        let mut applicability = Applicability::MachineApplicable;
+        let help_msgs = [
+            &format!("try replacing `map` with `{}`", method_to_use),
+            "and remove the `.flatten()`",
+        ];
+        let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability);
+        span_lint_and_sugg_for_edges(
             cx,
             MAP_FLATTEN,
-            expr.span.with_lo(recv.span.hi()),
-            "called `map(..).flatten()` on an `Iterator`",
-            &format!("try using `{}` instead", method_to_use),
-            hint,
-            Applicability::MachineApplicable,
+            expr.span.with_lo(map_span.lo()),
+            &format!("called `map(..).flatten()` on `{}`", caller_ty_name),
+            &help_msgs,
+            format!("{}({})", method_to_use, closure_snippet),
+            applicability,
         );
     }
+}
 
-    // lint if caller of `.map().flatten()` is an Option or Result
-    let caller_type = match cx.typeck_results().expr_ty(recv).kind() {
-        ty::Adt(adt, _) => {
+fn try_get_caller_ty_name_and_method_name(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    caller_expr: &Expr<'_>,
+    map_arg: &Expr<'_>,
+) -> Option<(&'static str, &'static str)> {
+    if is_trait_method(cx, expr, sym::Iterator) {
+        if is_map_to_option(cx, map_arg) {
+            // `(...).map(...)` has type `impl Iterator<Item=Option<...>>
+            Some(("Iterator", "filter_map"))
+        } else {
+            // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
+            Some(("Iterator", "flat_map"))
+        }
+    } else {
+        if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(caller_expr).kind() {
             if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) {
-                "Option"
+                return Some(("Option", "and_then"));
             } else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) {
-                "Result"
-            } else {
-                return;
+                return Some(("Result", "and_then"));
             }
-        },
-        _ => {
-            return;
-        },
-    };
+        }
+        None
+    }
+}
 
-    let func_snippet = snippet(cx, map_arg.span, "..");
-    let hint = format!(".and_then({})", func_snippet);
-    let lint_info = format!("called `map(..).flatten()` on an `{}`", caller_type);
-    span_lint_and_sugg(
-        cx,
-        MAP_FLATTEN,
-        expr.span.with_lo(recv.span.hi()),
-        &lint_info,
-        "try using `and_then` instead",
-        hint,
-        Applicability::MachineApplicable,
-    );
+fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool {
+    let map_closure_ty = cx.typeck_results().expr_ty(map_arg);
+    match map_closure_ty.kind() {
+        ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
+            let map_closure_sig = match map_closure_ty.kind() {
+                ty::Closure(_, substs) => substs.as_closure().sig(),
+                _ => map_closure_ty.fn_sig(cx.tcx),
+            };
+            let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output());
+            is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option)
+        },
+        _ => false,
+    }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 5edd22cd14c..9d4e1fa3994 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -45,6 +45,7 @@ mod option_as_ref_deref;
 mod option_map_or_none;
 mod option_map_unwrap_or;
 mod or_fun_call;
+mod or_then_unwrap;
 mod search_is_some;
 mod single_char_add_str;
 mod single_char_insert_string;
@@ -59,6 +60,7 @@ mod uninit_assumed_init;
 mod unnecessary_filter_map;
 mod unnecessary_fold;
 mod unnecessary_iter_cloned;
+mod unnecessary_join;
 mod unnecessary_lazy_eval;
 mod unnecessary_to_owned;
 mod unwrap_or_else_default;
@@ -780,6 +782,42 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Checks for `.or(…).unwrap()` calls to Options and Results.
+    ///
+    /// ### Why is this bad?
+    /// You should use `.unwrap_or(…)` instead for clarity.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let fallback = "fallback";
+    /// // Result
+    /// # type Error = &'static str;
+    /// # let result: Result<&str, Error> = Err("error");
+    /// let value = result.or::<Error>(Ok(fallback)).unwrap();
+    ///
+    /// // Option
+    /// # let option: Option<&str> = None;
+    /// let value = option.or(Some(fallback)).unwrap();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let fallback = "fallback";
+    /// // Result
+    /// # let result: Result<&str, &str> = Err("error");
+    /// let value = result.unwrap_or(fallback);
+    ///
+    /// // Option
+    /// # let option: Option<&str> = None;
+    /// let value = option.unwrap_or(fallback);
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub OR_THEN_UNWRAP,
+    complexity,
+    "checks for `.or(…).unwrap()` calls to Options and Results."
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
     /// etc., and suggests to use `unwrap_or_else` instead
     ///
@@ -1140,7 +1178,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.61.0"]
     pub ITER_WITH_DRAIN,
-    perf,
+    nursery,
     "replace `.drain(..)` with `.into_iter()`"
 }
 
@@ -2012,6 +2050,35 @@ declare_clippy_lint! {
     "unnecessary calls to `to_owned`-like functions"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
+    ///
+    /// ### Why is this bad?
+    /// `.collect::<String>()` is more concise and usually more performant
+    ///
+    /// ### Example
+    /// ```rust
+    /// let vector = vec!["hello",  "world"];
+    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
+    /// println!("{}", output);
+    /// ```
+    /// The correct use would be:
+    /// ```rust
+    /// let vector = vec!["hello",  "world"];
+    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
+    /// println!("{}", output);
+    /// ```
+    /// ### Known problems
+    /// While `.collect::<String>()` is more performant in most cases, there are cases where
+    /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
+    /// will prevent loop unrolling and will result in a negative performance impact.
+    #[clippy::version = "1.61.0"]
+    pub UNNECESSARY_JOIN,
+    pedantic,
+    "using `.collect::<Vec<String>>().join(\"\")` on an iterator"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -2039,6 +2106,7 @@ impl_lint_pass!(Methods => [
     OPTION_MAP_OR_NONE,
     BIND_INSTEAD_OF_MAP,
     OR_FUN_CALL,
+    OR_THEN_UNWRAP,
     EXPECT_FUN_CALL,
     CHARS_NEXT_CMP,
     CHARS_LAST_CMP,
@@ -2096,6 +2164,7 @@ impl_lint_pass!(Methods => [
     MANUAL_SPLIT_ONCE,
     NEEDLESS_SPLITN,
     UNNECESSARY_TO_OWNED,
+    UNNECESSARY_JOIN,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -2377,7 +2446,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 flat_map_option::check(cx, expr, arg, span);
             },
             (name @ "flatten", args @ []) => match method_call(recv) {
-                Some(("map", [recv, map_arg], _)) => map_flatten::check(cx, expr, recv, map_arg),
+                Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
                 Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
                 _ => {},
             },
@@ -2391,6 +2460,11 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("is_file", []) => filetype_is_file::check(cx, expr, recv),
             ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
             ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
+            ("join", [join_arg]) => {
+                if let Some(("collect", _, span)) = method_call(recv) {
+                    unnecessary_join::check(cx, expr, recv, join_arg, span);
+                }
+            },
             ("last", args @ []) | ("skip", args @ [_]) => {
                 if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
                     if let ("cloned", []) = (name2, args2) {
@@ -2474,6 +2548,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                     Some(("get_mut", [recv, get_arg], _)) => {
                         get_unwrap::check(cx, expr, recv, get_arg, true);
                     },
+                    Some(("or", [recv, or_arg], or_span)) => {
+                        or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
+                    },
                     _ => {},
                 }
                 unwrap_used::check(cx, expr, recv);
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs
new file mode 100644
index 00000000000..be5768c3545
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs
@@ -0,0 +1,68 @@
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{diagnostics::span_lint_and_sugg, is_lang_ctor};
+use rustc_errors::Applicability;
+use rustc_hir::{lang_items::LangItem, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::{sym, Span};
+
+use super::OR_THEN_UNWRAP;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    unwrap_expr: &Expr<'_>,
+    recv: &'tcx Expr<'tcx>,
+    or_arg: &'tcx Expr<'_>,
+    or_span: Span,
+) {
+    let ty = cx.typeck_results().expr_ty(recv); // get type of x (we later check if it's Option or Result)
+    let title;
+    let or_arg_content: Span;
+
+    if is_type_diagnostic_item(cx, ty, sym::Option) {
+        title = "found `.or(Some(…)).unwrap()`";
+        if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::OptionSome) {
+            or_arg_content = content;
+        } else {
+            return;
+        }
+    } else if is_type_diagnostic_item(cx, ty, sym::Result) {
+        title = "found `.or(Ok(…)).unwrap()`";
+        if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::ResultOk) {
+            or_arg_content = content;
+        } else {
+            return;
+        }
+    } else {
+        // Someone has implemented a struct with .or(...).unwrap() chaining,
+        // but it's not an Option or a Result, so bail
+        return;
+    }
+
+    let mut applicability = Applicability::MachineApplicable;
+    let suggestion = format!(
+        "unwrap_or({})",
+        snippet_with_applicability(cx, or_arg_content, "..", &mut applicability)
+    );
+
+    span_lint_and_sugg(
+        cx,
+        OR_THEN_UNWRAP,
+        unwrap_expr.span.with_lo(or_span.lo()),
+        title,
+        "try this",
+        suggestion,
+        applicability,
+    );
+}
+
+fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option<Span> {
+    if let ExprKind::Call(some_expr, [arg]) = expr.kind
+        && let ExprKind::Path(qpath) = &some_expr.kind
+        && is_lang_ctor(cx, qpath, item)
+    {
+        Some(arg.span)
+    } else {
+        None
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs
new file mode 100644
index 00000000000..973b8a7e6bf
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs
@@ -0,0 +1,41 @@
+use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item};
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{Ref, Slice};
+use rustc_span::{sym, Span};
+
+use super::UNNECESSARY_JOIN;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    join_self_arg: &'tcx Expr<'tcx>,
+    join_arg: &'tcx Expr<'tcx>,
+    span: Span,
+) {
+    let applicability = Applicability::MachineApplicable;
+    let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg);
+    if_chain! {
+        // the turbofish for collect is ::<Vec<String>>
+        if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind();
+        if let Slice(slice) = ref_type.kind();
+        if is_type_diagnostic_item(cx, *slice, sym::String);
+        // the argument for join is ""
+        if let ExprKind::Lit(spanned) = &join_arg.kind;
+        if let LitKind::Str(symbol, _) = spanned.node;
+        if symbol.is_empty();
+        then {
+            span_lint_and_sugg(
+                cx,
+                UNNECESSARY_JOIN,
+                span.with_hi(expr.span.hi()),
+                r#"called `.collect<Vec<String>>().join("")` on an iterator"#,
+                "try using",
+                "collect::<String>()".to_owned(),
+                applicability,
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
index 1e2765263c8..2369be70812 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{eager_or_lazy, usage};
@@ -48,20 +48,19 @@ pub(super) fn check<'tcx>(
                     Applicability::MaybeIncorrect
                 };
 
-                span_lint_and_sugg(
-                    cx,
-                    UNNECESSARY_LAZY_EVALUATIONS,
-                    expr.span,
-                    msg,
-                    &format!("use `{}` instead", simplify_using),
-                    format!(
-                        "{0}.{1}({2})",
-                        snippet(cx, recv.span, ".."),
-                        simplify_using,
-                        snippet(cx, body_expr.span, ".."),
-                    ),
-                    applicability,
-                );
+                // This is a duplicate of what's happening in clippy_lints::methods::method_call,
+                // which isn't ideal, We want to get the method call span,
+                // but prefer to avoid changing the signature of the function itself.
+                if let hir::ExprKind::MethodCall(_, _, span) = expr.kind {
+                    span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
+                        diag.span_suggestion(
+                            span,
+                            &format!("use `{}(..)` instead", simplify_using),
+                            format!("{}({})", simplify_using, snippet(cx, body_expr.span, "..")),
+                            applicability,
+                        );
+                    });
+                }
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index 7916fb8e3b4..1555758fc4a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -2,7 +2,9 @@ use super::implicit_clone::is_clone_like;
 use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
+use clippy_utils::ty::{
+    contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
+};
 use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
 use rustc_errors::Applicability;
 use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
@@ -114,7 +116,12 @@ fn check_addr_of_expr(
                     parent.span,
                     &format!("unnecessary use of `{}`", method_name),
                     "use",
-                    format!("{:&>width$}{}", "", receiver_snippet, width = n_target_refs - n_receiver_refs),
+                    format!(
+                        "{:&>width$}{}",
+                        "",
+                        receiver_snippet,
+                        width = n_target_refs - n_receiver_refs
+                    ),
                     Applicability::MachineApplicable,
                 );
                 return true;
@@ -182,20 +189,10 @@ fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name:
         if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
         if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
         then {
-            if unnecessary_iter_cloned::check_for_loop_iter(
-                cx,
-                parent,
-                method_name,
-                receiver,
-                true,
-            ) {
+            if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
                 return true;
             }
-            let cloned_or_copied = if is_copy(cx, item_ty) {
-                "copied"
-            } else {
-                "cloned"
-            };
+            let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" };
             // The next suggestion may be incorrect because the removal of the `to_owned`-like
             // function could cause the iterator to hold a reference to a resource that is used
             // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
@@ -243,10 +240,11 @@ fn check_other_call_arg<'tcx>(
         if if trait_predicate.def_id() == deref_trait_id {
             if let [projection_predicate] = projection_predicates[..] {
                 let normalized_ty =
-                    cx.tcx.subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term);
+                    cx.tcx
+                        .subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term);
                 implements_trait(cx, receiver_ty, deref_trait_id, &[])
-                    && get_associated_type(cx, receiver_ty, deref_trait_id,
-                    "Target").map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty)
+                    && get_associated_type(cx, receiver_ty, deref_trait_id, "Target")
+                        .map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty)
             } else {
                 false
             }
@@ -254,7 +252,7 @@ fn check_other_call_arg<'tcx>(
             let composed_substs = compose_substs(
                 cx,
                 &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
-                call_substs
+                call_substs,
             );
             implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
         } else {
@@ -264,6 +262,12 @@ fn check_other_call_arg<'tcx>(
         // `Target = T`.
         if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
         let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 });
+        // If the trait is `AsRef` and the input type variable `T` occurs in the output type, then
+        // `T` must not be instantiated with a reference
+        // (https://github.com/rust-lang/rust-clippy/issues/8507).
+        if (n_refs == 0 && !receiver_ty.is_ref())
+            || trait_predicate.def_id() != as_ref_trait_id
+            || !contains_ty(fn_sig.output(), input);
         if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
         then {
             span_lint_and_sugg(
@@ -339,11 +343,7 @@ fn get_input_traits_and_projections<'tcx>(
                 if let Some(arg) = substs.iter().next();
                 if let GenericArgKind::Type(arg_ty) = arg.unpack();
                 if arg_ty == input;
-                then {
-                    true
-                } else {
-                    false
-                }
+                then { true } else { false }
             }
         };
         match predicate.kind().skip_binder() {
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 9c776437d7f..ba1997e70e1 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -436,7 +436,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                             DerefTy::Path,
                             None,
                         ),
-                        Some(sym::Cow) => {
+                        Some(sym::Cow) if mutability == Mutability::Not => {
                             let ty_name = name.args
                                 .and_then(|args| {
                                     args.args.iter().find_map(|a| match a {
diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
index 961cdb317e7..66b79513032 100644
--- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
-use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind};
+use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -76,14 +76,13 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
         );
     }
 
-    for single_use in &single_use_usages {
-        if !imports_reused_with_self.contains(&single_use.0) {
-            let can_suggest = single_use.2;
+    for (name, span, can_suggest) in single_use_usages {
+        if !imports_reused_with_self.contains(&name) {
             if can_suggest {
                 span_lint_and_sugg(
                     cx,
                     SINGLE_COMPONENT_PATH_IMPORTS,
-                    single_use.1,
+                    span,
                     "this import is redundant",
                     "remove it entirely",
                     String::new(),
@@ -93,7 +92,7 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
                 span_lint_and_help(
                     cx,
                     SINGLE_COMPONENT_PATH_IMPORTS,
-                    single_use.1,
+                    span,
                     "this import is redundant",
                     None,
                     "remove this import",
@@ -124,14 +123,11 @@ fn track_uses(
         ItemKind::Use(use_tree) => {
             let segments = &use_tree.prefix.segments;
 
-            let should_report =
-                |name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited);
-
             // keep track of `use some_module;` usages
             if segments.len() == 1 {
                 if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
                     let name = segments[0].ident.name;
-                    if should_report(&name) {
+                    if !macros.contains(&name) {
                         single_use_usages.push((name, item.span, true));
                     }
                 }
@@ -146,7 +142,7 @@ fn track_uses(
                         if segments.len() == 1 {
                             if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
                                 let name = segments[0].ident.name;
-                                if should_report(&name) {
+                                if !macros.contains(&name) {
                                     single_use_usages.push((name, tree.0.span, false));
                                 }
                             }
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index 23cb9d40dfd..02569bd3a47 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -415,7 +415,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
                 // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
                 let const_context = in_constant(cx, e.hir_id);
 
-                let from_ty = cx.typeck_results().expr_ty(arg);
+                let from_ty = cx.typeck_results().expr_ty_adjusted(arg);
+                // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them.
                 let to_ty = cx.typeck_results().expr_ty(e);
 
                 // If useless_transmute is triggered, the other lints can be skipped.
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
index 6edff224092..f5e21267a89 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
@@ -3,8 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::is_c_void;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
-use rustc_middle::ty::subst::Subst;
-use rustc_middle::ty::{self, Ty, TypeAndMut};
+use rustc_middle::ty::subst::{Subst, SubstsRef};
+use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy};
 use rustc_span::Span;
 
 #[allow(clippy::too_many_lines)]
@@ -23,7 +23,8 @@ pub(super) fn check<'tcx>(
                 unsized_ty,
                 to_ty: to_sub_ty,
             } => match reduce_ty(cx, to_sub_ty) {
-                ReducedTy::IntArray | ReducedTy::TypeErasure => break,
+                ReducedTy::TypeErasure => break,
+                ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
                 ReducedTy::Ref(to_sub_ty) => {
                     from_ty = unsized_ty;
                     to_ty = to_sub_ty;
@@ -48,7 +49,8 @@ pub(super) fn check<'tcx>(
                 unsized_ty,
                 from_ty: from_sub_ty,
             } => match reduce_ty(cx, from_sub_ty) {
-                ReducedTy::IntArray | ReducedTy::TypeErasure => break,
+                ReducedTy::TypeErasure => break,
+                ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
                 ReducedTy::Ref(from_sub_ty) => {
                     from_ty = from_sub_ty;
                     to_ty = unsized_ty;
@@ -123,9 +125,19 @@ pub(super) fn check<'tcx>(
                 from_ty: from_sub_ty,
                 to_ty: to_sub_ty,
             } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
-                (ReducedTy::IntArray | ReducedTy::TypeErasure, _)
-                | (_, ReducedTy::IntArray | ReducedTy::TypeErasure) => return false,
+                (ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false,
                 (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
+                    let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
+                        = (from_ty.kind(), to_ty.kind())
+                        && from_def == to_def
+                    {
+                        if same_except_params(from_subs, to_subs) {
+                            return false;
+                        }
+                        Some(from_def.did())
+                    } else {
+                        None
+                    };
                     span_lint_and_then(
                         cx,
                         TRANSMUTE_UNDEFINED_REPR,
@@ -135,21 +147,17 @@ pub(super) fn check<'tcx>(
                             from_ty_orig, to_ty_orig
                         ),
                         |diag| {
-                            if_chain! {
-                                if let (Some(from_def), Some(to_def)) = (from_ty.ty_adt_def(), to_ty.ty_adt_def());
-                                if from_def == to_def;
-                                then {
-                                    diag.note(&format!(
-                                        "two instances of the same generic type (`{}`) may have different layouts",
-                                        cx.tcx.item_name(from_def.did())
-                                    ));
-                                } else {
-                                    if from_ty_orig.peel_refs() != from_ty {
-                                        diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
-                                    }
-                                    if to_ty_orig.peel_refs() != to_ty {
-                                        diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
-                                    }
+                            if let Some(same_adt_did) = same_adt_did {
+                                diag.note(&format!(
+                                    "two instances of the same generic type (`{}`) may have different layouts",
+                                    cx.tcx.item_name(same_adt_did)
+                                ));
+                            } else {
+                                if from_ty_orig.peel_refs() != from_ty {
+                                    diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
+                                }
+                                if to_ty_orig.peel_refs() != to_ty {
+                                    diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
                                 }
                             }
                         },
@@ -196,10 +204,13 @@ pub(super) fn check<'tcx>(
                     continue;
                 },
                 (
-                    ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
-                    ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
+                    ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
+                    ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
                 )
-                | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => break,
+                | (
+                    ReducedTy::UnorderedFields(_) | ReducedTy::Param,
+                    ReducedTy::UnorderedFields(_) | ReducedTy::Param,
+                ) => break,
             },
         }
     }
@@ -263,9 +274,8 @@ enum ReducedTy<'tcx> {
     UnorderedFields(Ty<'tcx>),
     /// The type is a reference to the contained type.
     Ref(Ty<'tcx>),
-    /// The type is an array of a primitive integer type. These can be used as storage for a value
-    /// of another type.
-    IntArray,
+    /// The type is a generic parameter.
+    Param,
     /// Any other type.
     Other(Ty<'tcx>),
 }
@@ -275,17 +285,18 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
     loop {
         ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
         return match *ty.kind() {
-            ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::IntArray,
+            ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::TypeErasure,
             ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
                 ty = sub_ty;
                 continue;
             },
             ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure,
             ty::Tuple(args) => {
-                let Some(sized_ty) = args.iter().find(|&ty| !is_zero_sized_ty(cx, ty)) else {
+                let mut iter = args.iter();
+                let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
                     return ReducedTy::OrderedFields(ty);
                 };
-                if args.iter().all(|ty| is_zero_sized_ty(cx, ty)) {
+                if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
                     ty = sized_ty;
                     continue;
                 }
@@ -313,9 +324,12 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
             ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => {
                 ReducedTy::TypeErasure
             },
+            // TODO: Check if the conversion to or from at least one of a union's fields is valid.
+            ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure,
             ty::Foreign(_) => ReducedTy::TypeErasure,
             ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
             ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
+            ty::Param(_) => ReducedTy::Param,
             _ => ReducedTy::Other(ty),
         };
     }
@@ -332,3 +346,27 @@ fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
         }
     }
 }
+
+fn is_size_pair(ty: Ty<'_>) -> bool {
+    if let ty::Tuple(tys) = *ty.kind()
+        && let [ty1, ty2] = &**tys
+    {
+        matches!(ty1.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
+            && matches!(ty2.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
+    } else {
+        false
+    }
+}
+
+fn same_except_params(subs1: SubstsRef<'_>, subs2: SubstsRef<'_>) -> bool {
+    // TODO: check const parameters as well. Currently this will consider `Array<5>` the same as
+    // `Array<6>`
+    for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) {
+        match (ty1.kind(), ty2.kind()) {
+            (ty::Param(_), _) | (_, ty::Param(_)) => (),
+            (ty::Adt(adt1, subs1), ty::Adt(adt2, subs2)) if adt1 == adt2 && same_except_params(subs1, subs2) => (),
+            _ => return false,
+        }
+    }
+    true
+}
diff --git a/src/tools/clippy/clippy_lints/src/try_err.rs b/src/tools/clippy/clippy_lints/src/try_err.rs
index 80d6f3c6336..e108f7be12e 100644
--- a/src/tools/clippy/clippy_lints/src/try_err.rs
+++ b/src/tools/clippy/clippy_lints/src/try_err.rs
@@ -43,7 +43,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.38.0"]
     pub TRY_ERR,
-    style,
+    restriction,
     "return errors explicitly rather than hiding them behind a `?`"
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index a617422bbeb..b3fad6ce7b6 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -85,7 +85,7 @@ macro_rules! CONFIGURATION_VALUE_TEMPLATE {
     };
 }
 
-const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
+const LINT_EMISSION_FUNCTIONS: [&[&str]; 8] = [
     &["clippy_utils", "diagnostics", "span_lint"],
     &["clippy_utils", "diagnostics", "span_lint_and_help"],
     &["clippy_utils", "diagnostics", "span_lint_and_note"],
@@ -93,6 +93,7 @@ const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
     &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
     &["clippy_utils", "diagnostics", "span_lint_and_then"],
     &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
+    &["clippy_utils", "diagnostics", "span_lint_and_sugg_for_edges"],
 ];
 const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
     ("span_suggestion", false),
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index 532bd810a2e..f3d818cc348 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -581,14 +581,19 @@ impl Write {
             };
 
             let replacement: String = match lit.token.kind {
-                LitKind::Integer | LitKind::Float | LitKind::Err => continue,
                 LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
                     lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
                 },
                 LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
                     lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
                 },
-                LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue,
+                LitKind::StrRaw(_)
+                | LitKind::Str
+                | LitKind::ByteStrRaw(_)
+                | LitKind::ByteStr
+                | LitKind::Integer
+                | LitKind::Float
+                | LitKind::Err => continue,
                 LitKind::Byte | LitKind::Char => match lit.token.symbol.as_str() {
                     "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"",
                     "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue,
diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs
index a927788e6a4..625a53899df 100644
--- a/src/tools/clippy/clippy_utils/src/diagnostics.rs
+++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs
@@ -8,7 +8,7 @@
 //! Thank you!
 //! ~The `INTERNAL_METADATA_COLLECTOR` lint
 
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::{emitter::MAX_SUGGESTION_HIGHLIGHT_LINES, Applicability, Diagnostic};
 use rustc_hir::HirId;
 use rustc_lint::{LateContext, Lint, LintContext};
 use rustc_span::source_map::{MultiSpan, Span};
@@ -213,6 +213,90 @@ pub fn span_lint_and_sugg<'a, T: LintContext>(
     });
 }
 
+/// Like [`span_lint_and_sugg`] with a focus on the edges. The output will either
+/// emit single span or multispan suggestion depending on the number of its lines.
+///
+/// If the given suggestion string has more lines than the maximum display length defined by
+/// [`MAX_SUGGESTION_HIGHLIGHT_LINES`][`rustc_errors::emitter::MAX_SUGGESTION_HIGHLIGHT_LINES`],
+/// this function will split the suggestion and span to showcase the change for the top and
+/// bottom edge of the code. For normal suggestions, in one display window, the help message
+/// will be combined with a colon.
+///
+/// Multipart suggestions like the one being created here currently cannot be
+/// applied by rustfix (See [rustfix#141](https://github.com/rust-lang/rustfix/issues/141)).
+/// Testing rustfix with this lint emission function might require a file with
+/// suggestions that can be fixed and those that can't. See
+/// [clippy#8520](https://github.com/rust-lang/rust-clippy/pull/8520/files) for
+/// an example and of this.
+///
+/// # Example for a long suggestion
+///
+/// ```text
+/// error: called `map(..).flatten()` on `Option`
+///   --> $DIR/map_flatten.rs:8:10
+///    |
+/// LL |           .map(|x| {
+///    |  __________^
+/// LL | |             if x <= 5 {
+/// LL | |                 Some(x)
+/// LL | |             } else {
+/// ...  |
+/// LL | |         })
+/// LL | |         .flatten();
+///    | |__________________^
+///    |
+///   = note: `-D clippy::map-flatten` implied by `-D warnings`
+/// help: try replacing `map` with `and_then`
+///    |
+/// LL ~         .and_then(|x| {
+/// LL +             if x <= 5 {
+/// LL +                 Some(x)
+///    |
+/// help: and remove the `.flatten()`
+///    |
+/// LL +                 None
+/// LL +             }
+/// LL ~         });
+///    |
+/// ```
+pub fn span_lint_and_sugg_for_edges(
+    cx: &LateContext<'_>,
+    lint: &'static Lint,
+    sp: Span,
+    msg: &str,
+    helps: &[&str; 2],
+    sugg: String,
+    applicability: Applicability,
+) {
+    span_lint_and_then(cx, lint, sp, msg, |diag| {
+        let sugg_lines_count = sugg.lines().count();
+        if sugg_lines_count > MAX_SUGGESTION_HIGHLIGHT_LINES {
+            let sm = cx.sess().source_map();
+            if let (Ok(line_upper), Ok(line_bottom)) = (sm.lookup_line(sp.lo()), sm.lookup_line(sp.hi())) {
+                let split_idx = MAX_SUGGESTION_HIGHLIGHT_LINES / 2;
+                let span_upper = sm.span_until_char(sp.with_hi(line_upper.sf.lines[line_upper.line + split_idx]), '\n');
+                let span_bottom = sp.with_lo(line_bottom.sf.lines[line_bottom.line - split_idx]);
+
+                let sugg_lines_vec = sugg.lines().collect::<Vec<&str>>();
+                let sugg_upper = sugg_lines_vec[..split_idx].join("\n");
+                let sugg_bottom = sugg_lines_vec[sugg_lines_count - split_idx..].join("\n");
+
+                diag.span_suggestion(span_upper, helps[0], sugg_upper, applicability);
+                diag.span_suggestion(span_bottom, helps[1], sugg_bottom, applicability);
+
+                return;
+            }
+        }
+        diag.span_suggestion_with_style(
+            sp,
+            &helps.join(", "),
+            sugg,
+            applicability,
+            rustc_errors::SuggestionStyle::ShowAlways,
+        );
+    });
+}
+
 /// Create a suggestion made from several `span → replacement`.
 ///
 /// Note: in the JSON format (used by `compiletest_rs`), the help message will
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 63c442e7008..1fc9979f3dd 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -808,7 +808,7 @@ pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'
             closure_arg_is_type_annotated_double_ref,
             next_pos: closure.span.lo(),
             suggestion_start: String::new(),
-            applicability: Applicability::MaybeIncorrect,
+            applicability: Applicability::MachineApplicable,
         };
 
         let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id);
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 9d5da4ed68f..5befb856a02 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2022-03-14"
+channel = "nightly-2022-03-24"
 components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/tests/ui/cast_enum_constructor.rs b/src/tools/clippy/tests/ui/cast_enum_constructor.rs
new file mode 100644
index 00000000000..0193454ad14
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_enum_constructor.rs
@@ -0,0 +1,17 @@
+#![warn(clippy::cast_enum_constructor)]
+#![allow(clippy::fn_to_numeric_cast)]
+
+fn main() {
+    enum Foo {
+        Y(u32),
+    }
+
+    enum Bar {
+        X,
+    }
+
+    let _ = Foo::Y as usize;
+    let _ = Foo::Y as isize;
+    let _ = Foo::Y as fn(u32) -> Foo;
+    let _ = Bar::X as usize;
+}
diff --git a/src/tools/clippy/tests/ui/cast_enum_constructor.stderr b/src/tools/clippy/tests/ui/cast_enum_constructor.stderr
new file mode 100644
index 00000000000..710909dd26f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_enum_constructor.stderr
@@ -0,0 +1,16 @@
+error: cast of an enum tuple constructor to an integer
+  --> $DIR/cast_enum_constructor.rs:13:13
+   |
+LL |     let _ = Foo::Y as usize;
+   |             ^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::cast-enum-constructor` implied by `-D warnings`
+
+error: cast of an enum tuple constructor to an integer
+  --> $DIR/cast_enum_constructor.rs:14:13
+   |
+LL |     let _ = Foo::Y as isize;
+   |             ^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5497.stderr b/src/tools/clippy/tests/ui/crashes/ice-5497.stderr
new file mode 100644
index 00000000000..e75e7dc9136
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5497.stderr
@@ -0,0 +1,10 @@
+error: this operation will panic at runtime
+  --> $DIR/ice-5497.rs:9:22
+   |
+LL |     const OOB: i32 = [1][1] + T::OOB;
+   |                      ^^^^^^ index out of bounds: the length is 1 but the index is 1
+   |
+   = note: `#[deny(unconditional_panic)]` on by default
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/map_flatten.rs b/src/tools/clippy/tests/ui/map_flatten.rs
index aa1f76e335a..7d47ee09dc1 100644
--- a/src/tools/clippy/tests/ui/map_flatten.rs
+++ b/src/tools/clippy/tests/ui/map_flatten.rs
@@ -1,31 +1,55 @@
-// run-rustfix
-
-#![warn(clippy::all, clippy::pedantic)]
-#![allow(clippy::let_underscore_drop)]
-#![allow(clippy::missing_docs_in_private_items)]
-#![allow(clippy::map_identity)]
-#![allow(clippy::redundant_closure)]
-#![allow(clippy::unnecessary_wraps)]
+#![warn(clippy::map_flatten)]
 #![feature(result_flattening)]
 
-fn main() {
-    // mapping to Option on Iterator
-    fn option_id(x: i8) -> Option<i8> {
-        Some(x)
-    }
-    let option_id_ref: fn(i8) -> Option<i8> = option_id;
-    let option_id_closure = |x| Some(x);
-    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
-    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
-    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
-    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
+// issue #8506, multi-line
+#[rustfmt::skip]
+fn long_span() {
+    let _: Option<i32> = Some(1)
+        .map(|x| {
+            if x <= 5 {
+                Some(x)
+            } else {
+                None
+            }
+        })
+        .flatten();
 
-    // mapping to Iterator on Iterator
-    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
+    let _: Result<i32, i32> = Ok(1)
+        .map(|x| {
+            if x == 1 {
+                Ok(x)
+            } else {
+                Err(0)
+            }
+        })
+        .flatten();
 
-    // mapping to Option on Option
-    let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
+    let result: Result<i32, i32> = Ok(2);
+    fn do_something() { }
+    let _: Result<i32, i32> = result
+        .map(|res| {
+            if res > 0 {
+                do_something();
+                Ok(res)
+            } else {
+                Err(0)
+            }
+        })
+        .flatten();
+        
+    let _: Vec<_> = vec![5_i8; 6]
+        .into_iter()
+        .map(|some_value| {
+            if some_value > 3 {
+                Some(some_value)
+            } else {
+                None
+            }
+        })
+        .flatten()
+        .collect();
+}
 
-    // mapping to Result on Result
-    let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
+fn main() {
+    long_span();
 }
diff --git a/src/tools/clippy/tests/ui/map_flatten.stderr b/src/tools/clippy/tests/ui/map_flatten.stderr
index bcd2047e6fa..c9c60df838f 100644
--- a/src/tools/clippy/tests/ui/map_flatten.stderr
+++ b/src/tools/clippy/tests/ui/map_flatten.stderr
@@ -1,46 +1,107 @@
-error: called `map(..).flatten()` on an `Iterator`
-  --> $DIR/map_flatten.rs:18:46
+error: called `map(..).flatten()` on `Option`
+  --> $DIR/map_flatten.rs:8:10
    |
-LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
-   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)`
+LL |           .map(|x| {
+   |  __________^
+LL | |             if x <= 5 {
+LL | |                 Some(x)
+LL | |             } else {
+...  |
+LL | |         })
+LL | |         .flatten();
+   | |__________________^
    |
    = note: `-D clippy::map-flatten` implied by `-D warnings`
-
-error: called `map(..).flatten()` on an `Iterator`
-  --> $DIR/map_flatten.rs:19:46
+help: try replacing `map` with `and_then`
    |
-LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
-   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)`
-
-error: called `map(..).flatten()` on an `Iterator`
-  --> $DIR/map_flatten.rs:20:46
+LL ~         .and_then(|x| {
+LL +             if x <= 5 {
+LL +                 Some(x)
    |
-LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
-   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)`
-
-error: called `map(..).flatten()` on an `Iterator`
-  --> $DIR/map_flatten.rs:21:46
+help: and remove the `.flatten()`
+   |
+LL +                 None
+LL +             }
+LL ~         });
    |
-LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
-   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))`
 
-error: called `map(..).flatten()` on an `Iterator`
-  --> $DIR/map_flatten.rs:24:46
+error: called `map(..).flatten()` on `Result`
+  --> $DIR/map_flatten.rs:18:10
+   |
+LL |           .map(|x| {
+   |  __________^
+LL | |             if x == 1 {
+LL | |                 Ok(x)
+LL | |             } else {
+...  |
+LL | |         })
+LL | |         .flatten();
+   | |__________________^
+   |
+help: try replacing `map` with `and_then`
+   |
+LL ~         .and_then(|x| {
+LL +             if x == 1 {
+LL +                 Ok(x)
+   |
+help: and remove the `.flatten()`
+   |
+LL +                 Err(0)
+LL +             }
+LL ~         });
    |
-LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
-   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)`
 
-error: called `map(..).flatten()` on an `Option`
-  --> $DIR/map_flatten.rs:27:39
+error: called `map(..).flatten()` on `Result`
+  --> $DIR/map_flatten.rs:30:10
+   |
+LL |           .map(|res| {
+   |  __________^
+LL | |             if res > 0 {
+LL | |                 do_something();
+LL | |                 Ok(res)
+...  |
+LL | |         })
+LL | |         .flatten();
+   | |__________________^
+   |
+help: try replacing `map` with `and_then`
+   |
+LL ~         .and_then(|res| {
+LL +             if res > 0 {
+LL +                 do_something();
+   |
+help: and remove the `.flatten()`
+   |
+LL +                 Err(0)
+LL +             }
+LL ~         });
    |
-LL |     let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
-   |                                       ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
 
-error: called `map(..).flatten()` on an `Result`
-  --> $DIR/map_flatten.rs:30:41
+error: called `map(..).flatten()` on `Iterator`
+  --> $DIR/map_flatten.rs:42:10
+   |
+LL |           .map(|some_value| {
+   |  __________^
+LL | |             if some_value > 3 {
+LL | |                 Some(some_value)
+LL | |             } else {
+...  |
+LL | |         })
+LL | |         .flatten()
+   | |__________________^
+   |
+help: try replacing `map` with `filter_map`
+   |
+LL ~         .filter_map(|some_value| {
+LL +             if some_value > 3 {
+LL +                 Some(some_value)
+   |
+help: and remove the `.flatten()`
+   |
+LL +                 None
+LL +             }
+LL +         })
    |
-LL |     let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
-   |                                         ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
 
-error: aborting due to 7 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/map_flatten.fixed b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed
index fec3a95edd6..fec3a95edd6 100644
--- a/src/tools/clippy/tests/ui/map_flatten.fixed
+++ b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed
diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.rs b/src/tools/clippy/tests/ui/map_flatten_fixable.rs
new file mode 100644
index 00000000000..aa1f76e335a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_flatten_fixable.rs
@@ -0,0 +1,31 @@
+// run-rustfix
+
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(clippy::let_underscore_drop)]
+#![allow(clippy::missing_docs_in_private_items)]
+#![allow(clippy::map_identity)]
+#![allow(clippy::redundant_closure)]
+#![allow(clippy::unnecessary_wraps)]
+#![feature(result_flattening)]
+
+fn main() {
+    // mapping to Option on Iterator
+    fn option_id(x: i8) -> Option<i8> {
+        Some(x)
+    }
+    let option_id_ref: fn(i8) -> Option<i8> = option_id;
+    let option_id_closure = |x| Some(x);
+    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
+    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
+    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
+    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
+
+    // mapping to Iterator on Iterator
+    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
+
+    // mapping to Option on Option
+    let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
+
+    // mapping to Result on Result
+    let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
+}
diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.stderr b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr
new file mode 100644
index 00000000000..c91c73846b6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr
@@ -0,0 +1,80 @@
+error: called `map(..).flatten()` on `Iterator`
+  --> $DIR/map_flatten_fixable.rs:18:47
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
+   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::map-flatten` implied by `-D warnings`
+help: try replacing `map` with `filter_map`, and remove the `.flatten()`
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect();
+   |                                               ~~~~~~~~~~~~~~~~~~~~~
+
+error: called `map(..).flatten()` on `Iterator`
+  --> $DIR/map_flatten_fixable.rs:19:47
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
+   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try replacing `map` with `filter_map`, and remove the `.flatten()`
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect();
+   |                                               ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: called `map(..).flatten()` on `Iterator`
+  --> $DIR/map_flatten_fixable.rs:20:47
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
+   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try replacing `map` with `filter_map`, and remove the `.flatten()`
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect();
+   |                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: called `map(..).flatten()` on `Iterator`
+  --> $DIR/map_flatten_fixable.rs:21:47
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
+   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try replacing `map` with `filter_map`, and remove the `.flatten()`
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect();
+   |                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: called `map(..).flatten()` on `Iterator`
+  --> $DIR/map_flatten_fixable.rs:24:47
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
+   |                                               ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try replacing `map` with `flat_map`, and remove the `.flatten()`
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
+   |                                               ~~~~~~~~~~~~~~~~~~
+
+error: called `map(..).flatten()` on `Option`
+  --> $DIR/map_flatten_fixable.rs:27:40
+   |
+LL |     let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
+   |                                        ^^^^^^^^^^^^^^^^^^^^
+   |
+help: try replacing `map` with `and_then`, and remove the `.flatten()`
+   |
+LL |     let _: Option<_> = (Some(Some(1))).and_then(|x| x);
+   |                                        ~~~~~~~~~~~~~~~
+
+error: called `map(..).flatten()` on `Result`
+  --> $DIR/map_flatten_fixable.rs:30:42
+   |
+LL |     let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
+   |                                          ^^^^^^^^^^^^^^^^^^^^
+   |
+help: try replacing `map` with `and_then`, and remove the `.flatten()`
+   |
+LL |     let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x);
+   |                                          ~~~~~~~~~~~~~~~
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr
index 7752a8a6ff2..b6d04263b37 100644
--- a/src/tools/clippy/tests/ui/match_same_arms.stderr
+++ b/src/tools/clippy/tests/ui/match_same_arms.stderr
@@ -1,128 +1,121 @@
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms.rs:13:14
+error: this match arm has an identical body to the `_` wildcard arm
+  --> $DIR/match_same_arms.rs:11:9
    |
-LL |         _ => 0, //~ ERROR match arms have same body
-   |              ^
+LL |         Abc::A => 0,
+   |         ^^^^^^^^^^^ help: try removing the arm
    |
    = note: `-D clippy::match-same-arms` implied by `-D warnings`
-note: same as this
-  --> $DIR/match_same_arms.rs:11:19
+   = help: or try changing either arm body
+note: `_` wildcard arm here
+  --> $DIR/match_same_arms.rs:13:9
    |
-LL |         Abc::A => 0,
-   |                   ^
-note: `Abc::A` has the same arm body as the `_` wildcard, consider removing it
-  --> $DIR/match_same_arms.rs:11:19
-   |
-LL |         Abc::A => 0,
-   |                   ^
+LL |         _ => 0, //~ ERROR match arms have same body
+   |         ^^^^^^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms.rs:18:20
-   |
-LL |         (.., 3) => 42, //~ ERROR match arms have same body
-   |                    ^^
-   |
-note: same as this
-  --> $DIR/match_same_arms.rs:17:23
-   |
-LL |         (1, .., 3) => 42,
-   |                       ^^
-help: consider refactoring into `(1, .., 3) | (.., 3)`
+error: this match arm has an identical body to another arm
   --> $DIR/match_same_arms.rs:17:9
    |
 LL |         (1, .., 3) => 42,
-   |         ^^^^^^^^^^
-   = help: ...or consider changing the match arm bodies
+   |         ----------^^^^^^
+   |         |
+   |         help: try merging the arm patterns: `(1, .., 3) | (.., 3)`
+   |
+   = help: or try changing either arm body
+note: other arm here
+  --> $DIR/match_same_arms.rs:18:9
+   |
+LL |         (.., 3) => 42, //~ ERROR match arms have same body
+   |         ^^^^^^^^^^^^^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms.rs:24:15
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms.rs:24:9
    |
 LL |         51 => 1, //~ ERROR match arms have same body
-   |               ^
+   |         --^^^^^
+   |         |
+   |         help: try merging the arm patterns: `51 | 42`
    |
-note: same as this
-  --> $DIR/match_same_arms.rs:23:15
-   |
-LL |         42 => 1,
-   |               ^
-help: consider refactoring into `42 | 51`
+   = help: or try changing either arm body
+note: other arm here
   --> $DIR/match_same_arms.rs:23:9
    |
 LL |         42 => 1,
-   |         ^^
-   = help: ...or consider changing the match arm bodies
+   |         ^^^^^^^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms.rs:26:15
-   |
-LL |         52 => 2, //~ ERROR match arms have same body
-   |               ^
-   |
-note: same as this
-  --> $DIR/match_same_arms.rs:25:15
-   |
-LL |         41 => 2,
-   |               ^
-help: consider refactoring into `41 | 52`
+error: this match arm has an identical body to another arm
   --> $DIR/match_same_arms.rs:25:9
    |
 LL |         41 => 2,
-   |         ^^
-   = help: ...or consider changing the match arm bodies
+   |         --^^^^^
+   |         |
+   |         help: try merging the arm patterns: `41 | 52`
+   |
+   = help: or try changing either arm body
+note: other arm here
+  --> $DIR/match_same_arms.rs:26:9
+   |
+LL |         52 => 2, //~ ERROR match arms have same body
+   |         ^^^^^^^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms.rs:32:14
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms.rs:32:9
    |
 LL |         2 => 2, //~ ERROR 2nd matched arms have same body
-   |              ^
-   |
-note: same as this
-  --> $DIR/match_same_arms.rs:31:14
+   |         -^^^^^
+   |         |
+   |         help: try merging the arm patterns: `2 | 1`
    |
-LL |         1 => 2,
-   |              ^
-help: consider refactoring into `1 | 2`
+   = help: or try changing either arm body
+note: other arm here
   --> $DIR/match_same_arms.rs:31:9
    |
 LL |         1 => 2,
-   |         ^
-   = help: ...or consider changing the match arm bodies
+   |         ^^^^^^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms.rs:33:14
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms.rs:33:9
    |
 LL |         3 => 2, //~ ERROR 3rd matched arms have same body
-   |              ^
-   |
-note: same as this
-  --> $DIR/match_same_arms.rs:31:14
+   |         -^^^^^
+   |         |
+   |         help: try merging the arm patterns: `3 | 1`
    |
-LL |         1 => 2,
-   |              ^
-help: consider refactoring into `1 | 3`
+   = help: or try changing either arm body
+note: other arm here
   --> $DIR/match_same_arms.rs:31:9
    |
 LL |         1 => 2,
-   |         ^
-   = help: ...or consider changing the match arm bodies
+   |         ^^^^^^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms.rs:50:55
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms.rs:32:9
    |
-LL |                 CommandInfo::External { name, .. } => name.to_string(),
-   |                                                       ^^^^^^^^^^^^^^^^
+LL |         2 => 2, //~ ERROR 2nd matched arms have same body
+   |         -^^^^^
+   |         |
+   |         help: try merging the arm patterns: `2 | 3`
    |
-note: same as this
-  --> $DIR/match_same_arms.rs:49:54
+   = help: or try changing either arm body
+note: other arm here
+  --> $DIR/match_same_arms.rs:33:9
    |
-LL |                 CommandInfo::BuiltIn { name, .. } => name.to_string(),
-   |                                                      ^^^^^^^^^^^^^^^^
-help: consider refactoring into `CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. }`
+LL |         3 => 2, //~ ERROR 3rd matched arms have same body
+   |         ^^^^^^
+
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms.rs:50:17
+   |
+LL |                 CommandInfo::External { name, .. } => name.to_string(),
+   |                 ----------------------------------^^^^^^^^^^^^^^^^^^^^
+   |                 |
+   |                 help: try merging the arm patterns: `CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. }`
+   |
+   = help: or try changing either arm body
+note: other arm here
   --> $DIR/match_same_arms.rs:49:17
    |
 LL |                 CommandInfo::BuiltIn { name, .. } => name.to_string(),
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = help: ...or consider changing the match arm bodies
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 7 previous errors
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs
index 67e1d518483..dbfeb4379d5 100644
--- a/src/tools/clippy/tests/ui/match_same_arms2.rs
+++ b/src/tools/clippy/tests/ui/match_same_arms2.rs
@@ -174,4 +174,57 @@ fn main() {
         Some(2) => 2,
         _ => 1,
     };
+
+    enum Foo {
+        X(u32),
+        Y(u32),
+        Z(u32),
+    }
+
+    // Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between.
+    let _ = match Foo::X(0) {
+        Foo::X(0) => 1,
+        Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2,
+        Foo::Z(_) => 1,
+        _ => 0,
+    };
+
+    // Suggest moving `Foo::Z(_)` up.
+    let _ = match Foo::X(0) {
+        Foo::X(0) => 1,
+        Foo::X(_) | Foo::Y(_) => 2,
+        Foo::Z(_) => 1,
+        _ => 0,
+    };
+
+    // Suggest moving `Foo::X(0)` down.
+    let _ = match Foo::X(0) {
+        Foo::X(0) => 1,
+        Foo::Y(_) | Foo::Z(0) => 2,
+        Foo::Z(_) => 1,
+        _ => 0,
+    };
+
+    // Don't lint.
+    let _ = match 0 {
+        -2 => 1,
+        -5..=50 => 2,
+        -150..=88 => 1,
+        _ => 3,
+    };
+
+    struct Bar {
+        x: u32,
+        y: u32,
+        z: u32,
+    }
+
+    // Lint.
+    let _ = match None {
+        Some(Bar { x: 0, y: 5, .. }) => 1,
+        Some(Bar { y: 10, z: 0, .. }) => 2,
+        None => 50,
+        Some(Bar { y: 0, x: 5, .. }) => 1,
+        _ => 200,
+    };
 }
diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr
index e1ed12f9370..14a672ba2fe 100644
--- a/src/tools/clippy/tests/ui/match_same_arms2.stderr
+++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr
@@ -1,175 +1,138 @@
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms2.rs:20:14
+error: this match arm has an identical body to the `_` wildcard arm
+  --> $DIR/match_same_arms2.rs:11:9
    |
-LL |           _ => {
-   |  ______________^
-LL | |             //~ ERROR match arms have same body
+LL | /         42 => {
 LL | |             foo();
 LL | |             let mut a = 42 + [23].len() as i32;
+LL | |             if true {
 ...  |
 LL | |             a
 LL | |         },
-   | |_________^
+   | |_________^ help: try removing the arm
    |
    = note: `-D clippy::match-same-arms` implied by `-D warnings`
-note: same as this
-  --> $DIR/match_same_arms2.rs:11:15
+   = help: or try changing either arm body
+note: `_` wildcard arm here
+  --> $DIR/match_same_arms2.rs:20:9
    |
-LL |           42 => {
-   |  _______________^
-LL | |             foo();
-LL | |             let mut a = 42 + [23].len() as i32;
-LL | |             if true {
-...  |
-LL | |             a
-LL | |         },
-   | |_________^
-note: `42` has the same arm body as the `_` wildcard, consider removing it
-  --> $DIR/match_same_arms2.rs:11:15
-   |
-LL |           42 => {
-   |  _______________^
+LL | /         _ => {
+LL | |             //~ ERROR match arms have same body
 LL | |             foo();
 LL | |             let mut a = 42 + [23].len() as i32;
-LL | |             if true {
 ...  |
 LL | |             a
 LL | |         },
    | |_________^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms2.rs:34:15
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms2.rs:34:9
    |
 LL |         51 => foo(), //~ ERROR match arms have same body
-   |               ^^^^^
+   |         --^^^^^^^^^
+   |         |
+   |         help: try merging the arm patterns: `51 | 42`
    |
-note: same as this
-  --> $DIR/match_same_arms2.rs:33:15
-   |
-LL |         42 => foo(),
-   |               ^^^^^
-help: consider refactoring into `42 | 51`
+   = help: or try changing either arm body
+note: other arm here
   --> $DIR/match_same_arms2.rs:33:9
    |
 LL |         42 => foo(),
-   |         ^^
-   = help: ...or consider changing the match arm bodies
+   |         ^^^^^^^^^^^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms2.rs:40:17
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms2.rs:40:9
    |
 LL |         None => 24, //~ ERROR match arms have same body
-   |                 ^^
+   |         ----^^^^^^
+   |         |
+   |         help: try merging the arm patterns: `None | Some(_)`
    |
-note: same as this
-  --> $DIR/match_same_arms2.rs:39:20
-   |
-LL |         Some(_) => 24,
-   |                    ^^
-help: consider refactoring into `Some(_) | None`
+   = help: or try changing either arm body
+note: other arm here
   --> $DIR/match_same_arms2.rs:39:9
    |
 LL |         Some(_) => 24,
-   |         ^^^^^^^
-   = help: ...or consider changing the match arm bodies
+   |         ^^^^^^^^^^^^^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms2.rs:62:28
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms2.rs:62:9
    |
 LL |         (None, Some(a)) => bar(a), //~ ERROR match arms have same body
-   |                            ^^^^^^
-   |
-note: same as this
-  --> $DIR/match_same_arms2.rs:61:28
+   |         ---------------^^^^^^^^^^
+   |         |
+   |         help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)`
    |
-LL |         (Some(a), None) => bar(a),
-   |                            ^^^^^^
-help: consider refactoring into `(Some(a), None) | (None, Some(a))`
+   = help: or try changing either arm body
+note: other arm here
   --> $DIR/match_same_arms2.rs:61:9
    |
 LL |         (Some(a), None) => bar(a),
-   |         ^^^^^^^^^^^^^^^
-   = help: ...or consider changing the match arm bodies
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms2.rs:68:26
-   |
-LL |         (.., Some(a)) => bar(a), //~ ERROR match arms have same body
-   |                          ^^^^^^
-   |
-note: same as this
-  --> $DIR/match_same_arms2.rs:67:26
-   |
-LL |         (Some(a), ..) => bar(a),
-   |                          ^^^^^^
-help: consider refactoring into `(Some(a), ..) | (.., Some(a))`
+error: this match arm has an identical body to another arm
   --> $DIR/match_same_arms2.rs:67:9
    |
 LL |         (Some(a), ..) => bar(a),
-   |         ^^^^^^^^^^^^^
-   = help: ...or consider changing the match arm bodies
-
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms2.rs:102:29
-   |
-LL |         (Ok(_), Some(x)) => println!("ok {}", x),
-   |                             ^^^^^^^^^^^^^^^^^^^^
+   |         -------------^^^^^^^^^^
+   |         |
+   |         help: try merging the arm patterns: `(Some(a), ..) | (.., Some(a))`
    |
-note: same as this
-  --> $DIR/match_same_arms2.rs:101:29
+   = help: or try changing either arm body
+note: other arm here
+  --> $DIR/match_same_arms2.rs:68:9
    |
-LL |         (Ok(x), Some(_)) => println!("ok {}", x),
-   |                             ^^^^^^^^^^^^^^^^^^^^
-help: consider refactoring into `(Ok(x), Some(_)) | (Ok(_), Some(x))`
+LL |         (.., Some(a)) => bar(a), //~ ERROR match arms have same body
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
   --> $DIR/match_same_arms2.rs:101:9
    |
 LL |         (Ok(x), Some(_)) => println!("ok {}", x),
-   |         ^^^^^^^^^^^^^^^^
-   = help: ...or consider changing the match arm bodies
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+   |         ----------------^^^^^^^^^^^^^^^^^^^^^^^^
+   |         |
+   |         help: try merging the arm patterns: `(Ok(x), Some(_)) | (Ok(_), Some(x))`
+   |
+   = help: or try changing either arm body
+note: other arm here
+  --> $DIR/match_same_arms2.rs:102:9
+   |
+LL |         (Ok(_), Some(x)) => println!("ok {}", x),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms2.rs:117:18
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms2.rs:117:9
    |
 LL |         Ok(_) => println!("ok"),
-   |                  ^^^^^^^^^^^^^^
-   |
-note: same as this
-  --> $DIR/match_same_arms2.rs:116:18
+   |         -----^^^^^^^^^^^^^^^^^^
+   |         |
+   |         help: try merging the arm patterns: `Ok(_) | Ok(3)`
    |
-LL |         Ok(3) => println!("ok"),
-   |                  ^^^^^^^^^^^^^^
-help: consider refactoring into `Ok(3) | Ok(_)`
+   = help: or try changing either arm body
+note: other arm here
   --> $DIR/match_same_arms2.rs:116:9
    |
 LL |         Ok(3) => println!("ok"),
-   |         ^^^^^
-   = help: ...or consider changing the match arm bodies
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: this `match` has identical arm bodies
-  --> $DIR/match_same_arms2.rs:144:14
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms2.rs:144:9
    |
 LL |           1 => {
-   |  ______________^
+   |           ^ help: try merging the arm patterns: `1 | 0`
+   |  _________|
+   | |
 LL | |             empty!(0);
 LL | |         },
    | |_________^
    |
-note: same as this
-  --> $DIR/match_same_arms2.rs:141:14
+   = help: or try changing either arm body
+note: other arm here
+  --> $DIR/match_same_arms2.rs:141:9
    |
-LL |           0 => {
-   |  ______________^
+LL | /         0 => {
 LL | |             empty!(0);
 LL | |         },
    | |_________^
-help: consider refactoring into `0 | 1`
-  --> $DIR/match_same_arms2.rs:141:9
-   |
-LL |         0 => {
-   |         ^
-   = help: ...or consider changing the match arm bodies
 
 error: match expression looks like `matches!` macro
   --> $DIR/match_same_arms2.rs:162:16
@@ -184,5 +147,50 @@ LL | |     };
    |
    = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
 
-error: aborting due to 9 previous errors
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms2.rs:194:9
+   |
+LL |         Foo::X(0) => 1,
+   |         ---------^^^^^
+   |         |
+   |         help: try merging the arm patterns: `Foo::X(0) | Foo::Z(_)`
+   |
+   = help: or try changing either arm body
+note: other arm here
+  --> $DIR/match_same_arms2.rs:196:9
+   |
+LL |         Foo::Z(_) => 1,
+   |         ^^^^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms2.rs:204:9
+   |
+LL |         Foo::Z(_) => 1,
+   |         ---------^^^^^
+   |         |
+   |         help: try merging the arm patterns: `Foo::Z(_) | Foo::X(0)`
+   |
+   = help: or try changing either arm body
+note: other arm here
+  --> $DIR/match_same_arms2.rs:202:9
+   |
+LL |         Foo::X(0) => 1,
+   |         ^^^^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
+  --> $DIR/match_same_arms2.rs:227:9
+   |
+LL |         Some(Bar { y: 0, x: 5, .. }) => 1,
+   |         ----------------------------^^^^^
+   |         |
+   |         help: try merging the arm patterns: `Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. })`
+   |
+   = help: or try changing either arm body
+note: other arm here
+  --> $DIR/match_same_arms2.rs:224:9
+   |
+LL |         Some(Bar { x: 0, y: 5, .. }) => 1,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
 
diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.fixed b/src/tools/clippy/tests/ui/or_then_unwrap.fixed
new file mode 100644
index 00000000000..27d4b795a5e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/or_then_unwrap.fixed
@@ -0,0 +1,52 @@
+// run-rustfix
+
+#![warn(clippy::or_then_unwrap)]
+#![allow(clippy::map_identity)]
+
+struct SomeStruct {}
+impl SomeStruct {
+    fn or(self, _: Option<Self>) -> Self {
+        self
+    }
+    fn unwrap(&self) {}
+}
+
+struct SomeOtherStruct {}
+impl SomeOtherStruct {
+    fn or(self) -> Self {
+        self
+    }
+    fn unwrap(&self) {}
+}
+
+fn main() {
+    let option: Option<&str> = None;
+    let _ = option.unwrap_or("fallback"); // should trigger lint
+
+    let result: Result<&str, &str> = Err("Error");
+    let _ = result.unwrap_or("fallback"); // should trigger lint
+
+    // as part of a method chain
+    let option: Option<&str> = None;
+    let _ = option.map(|v| v).unwrap_or("fallback").to_string().chars(); // should trigger lint
+
+    // Not Option/Result
+    let instance = SomeStruct {};
+    let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint
+
+    // or takes no argument
+    let instance = SomeOtherStruct {};
+    let _ = instance.or().unwrap(); // should not trigger lint and should not panic
+
+    // None in or
+    let option: Option<&str> = None;
+    let _ = option.or(None).unwrap(); // should not trigger lint
+
+    // Not Err in or
+    let result: Result<&str, &str> = Err("Error");
+    let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint
+
+    // other function between
+    let option: Option<&str> = None;
+    let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint
+}
diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.rs b/src/tools/clippy/tests/ui/or_then_unwrap.rs
new file mode 100644
index 00000000000..0dab5ae2f1c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/or_then_unwrap.rs
@@ -0,0 +1,52 @@
+// run-rustfix
+
+#![warn(clippy::or_then_unwrap)]
+#![allow(clippy::map_identity)]
+
+struct SomeStruct {}
+impl SomeStruct {
+    fn or(self, _: Option<Self>) -> Self {
+        self
+    }
+    fn unwrap(&self) {}
+}
+
+struct SomeOtherStruct {}
+impl SomeOtherStruct {
+    fn or(self) -> Self {
+        self
+    }
+    fn unwrap(&self) {}
+}
+
+fn main() {
+    let option: Option<&str> = None;
+    let _ = option.or(Some("fallback")).unwrap(); // should trigger lint
+
+    let result: Result<&str, &str> = Err("Error");
+    let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint
+
+    // as part of a method chain
+    let option: Option<&str> = None;
+    let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint
+
+    // Not Option/Result
+    let instance = SomeStruct {};
+    let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint
+
+    // or takes no argument
+    let instance = SomeOtherStruct {};
+    let _ = instance.or().unwrap(); // should not trigger lint and should not panic
+
+    // None in or
+    let option: Option<&str> = None;
+    let _ = option.or(None).unwrap(); // should not trigger lint
+
+    // Not Err in or
+    let result: Result<&str, &str> = Err("Error");
+    let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint
+
+    // other function between
+    let option: Option<&str> = None;
+    let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint
+}
diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.stderr b/src/tools/clippy/tests/ui/or_then_unwrap.stderr
new file mode 100644
index 00000000000..da88154c59f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/or_then_unwrap.stderr
@@ -0,0 +1,22 @@
+error: found `.or(Some(…)).unwrap()`
+  --> $DIR/or_then_unwrap.rs:24:20
+   |
+LL |     let _ = option.or(Some("fallback")).unwrap(); // should trigger lint
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")`
+   |
+   = note: `-D clippy::or-then-unwrap` implied by `-D warnings`
+
+error: found `.or(Ok(…)).unwrap()`
+  --> $DIR/or_then_unwrap.rs:27:20
+   |
+LL |     let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")`
+
+error: found `.or(Some(…)).unwrap()`
+  --> $DIR/or_then_unwrap.rs:31:31
+   |
+LL |     let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs
index 97990fedd51..03dd938a233 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.rs
+++ b/src/tools/clippy/tests/ui/ptr_arg.rs
@@ -194,3 +194,10 @@ fn two_vecs(a: &mut Vec<u32>, b: &mut Vec<u32>) {
     a.push(0);
     b.push(1);
 }
+
+// Issue #8495
+fn cow_conditional_to_mut(a: &mut Cow<str>) {
+    if a.is_empty() {
+        a.to_mut().push_str("foo");
+    }
+}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_macro.fixed b/src/tools/clippy/tests/ui/single_component_path_imports_macro.fixed
deleted file mode 100644
index e43f5d381aa..00000000000
--- a/src/tools/clippy/tests/ui/single_component_path_imports_macro.fixed
+++ /dev/null
@@ -1,20 +0,0 @@
-// run-rustfix
-#![warn(clippy::single_component_path_imports)]
-#![allow(unused_imports)]
-
-// #7106: use statements exporting a macro within a crate should not trigger lint
-
-macro_rules! m1 {
-    () => {};
-}
-pub(crate) use m1; // ok
-
-macro_rules! m2 {
-    () => {};
-}
- // fail
-
-fn main() {
-    m1!();
-    m2!();
-}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs b/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs
index 3c65ca3054c..fda294a6154 100644
--- a/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs
+++ b/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs
@@ -1,8 +1,8 @@
-// run-rustfix
 #![warn(clippy::single_component_path_imports)]
 #![allow(unused_imports)]
 
 // #7106: use statements exporting a macro within a crate should not trigger lint
+// #7923: normal `use` statements of macros should also not trigger the lint
 
 macro_rules! m1 {
     () => {};
@@ -12,7 +12,7 @@ pub(crate) use m1; // ok
 macro_rules! m2 {
     () => {};
 }
-use m2; // fail
+use m2; // ok
 
 fn main() {
     m1!();
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_macro.stderr b/src/tools/clippy/tests/ui/single_component_path_imports_macro.stderr
deleted file mode 100644
index 37d5176129f..00000000000
--- a/src/tools/clippy/tests/ui/single_component_path_imports_macro.stderr
+++ /dev/null
@@ -1,10 +0,0 @@
-error: this import is redundant
-  --> $DIR/single_component_path_imports_macro.rs:15:1
-   |
-LL | use m2; // fail
-   | ^^^^^^^ help: remove it entirely
-   |
-   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
-
-error: aborting due to previous error
-
diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs
index b163d605634..b06ed4a9173 100644
--- a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs
+++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs
@@ -1,8 +1,9 @@
 #![warn(clippy::transmute_undefined_repr)]
 #![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)]
 
+use core::any::TypeId;
 use core::ffi::c_void;
-use core::mem::{size_of, transmute};
+use core::mem::{size_of, transmute, MaybeUninit};
 
 fn value<T>() -> T {
     unimplemented!()
@@ -87,5 +88,57 @@ fn main() {
 
         let _: *const [u8] = transmute(value::<Box<[u8]>>()); // Ok
         let _: Box<[u8]> = transmute(value::<*mut [u8]>()); // Ok
+
+        let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>,)>()); // Ok
+        let _: (Ty2<u32, u32>,) = transmute(value::<Ty2<u32, u32>>()); // Ok
+
+        let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>, ())>()); // Ok
+        let _: (Ty2<u32, u32>, ()) = transmute(value::<Ty2<u32, u32>>()); // Ok
+
+        let _: Ty2<u32, u32> = transmute(value::<((), Ty2<u32, u32>)>()); // Ok
+        let _: ((), Ty2<u32, u32>) = transmute(value::<Ty2<u32, u32>>()); // Ok
+
+        let _: (usize, usize) = transmute(value::<&[u8]>()); // Ok
+        let _: &[u8] = transmute(value::<(usize, usize)>()); // Ok
+
+        trait Trait {}
+        let _: (isize, isize) = transmute(value::<&dyn Trait>()); // Ok
+        let _: &dyn Trait = transmute(value::<(isize, isize)>()); // Ok
+
+        let _: MaybeUninit<Ty2<u32, u32>> = transmute(value::<Ty2<u32, u32>>()); // Ok
+        let _: Ty2<u32, u32> = transmute(value::<MaybeUninit<Ty2<u32, u32>>>()); // Ok
+
+        let _: Ty<&[u32]> = transmute::<&[u32], _>(value::<&Vec<u32>>()); // Ok
+    }
+}
+
+fn _with_generics<T: 'static, U: 'static>() {
+    if TypeId::of::<T>() != TypeId::of::<u32>() || TypeId::of::<T>() != TypeId::of::<U>() {
+        return;
+    }
+    unsafe {
+        let _: &u32 = transmute(value::<&T>()); // Ok
+        let _: &T = transmute(value::<&u32>()); // Ok
+
+        let _: Vec<U> = transmute(value::<Vec<T>>()); // Ok
+        let _: Vec<T> = transmute(value::<Vec<U>>()); // Ok
+
+        let _: Ty<&u32> = transmute(value::<&T>()); // Ok
+        let _: Ty<&T> = transmute(value::<&u32>()); // Ok
+
+        let _: Vec<u32> = transmute(value::<Vec<T>>()); // Ok
+        let _: Vec<T> = transmute(value::<Vec<u32>>()); // Ok
+
+        let _: &Ty2<u32, u32> = transmute(value::<&Ty2<T, U>>()); // Ok
+        let _: &Ty2<T, U> = transmute(value::<&Ty2<u32, u32>>()); // Ok
+
+        let _: Vec<Vec<u32>> = transmute(value::<Vec<Vec<T>>>()); // Ok
+        let _: Vec<Vec<T>> = transmute(value::<Vec<Vec<u32>>>()); // Ok
+
+        let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
+        let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
+
+        let _: *const u32 = transmute(value::<Box<T>>()); // Ok
+        let _: Box<T> = transmute(value::<*const u32>()); // Ok
     }
 }
diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr b/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr
index 42d544fc954..28bfba6c757 100644
--- a/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr
+++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr
@@ -1,5 +1,5 @@
 error: transmute from `Ty2<u32, i32>` which has an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:26:33
+  --> $DIR/transmute_undefined_repr.rs:27:33
    |
 LL |         let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,13 +7,13 @@ LL |         let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lin
    = note: `-D clippy::transmute-undefined-repr` implied by `-D warnings`
 
 error: transmute into `Ty2<u32, i32>` which has an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:27:32
+  --> $DIR/transmute_undefined_repr.rs:28:32
    |
 LL |         let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:32:32
+  --> $DIR/transmute_undefined_repr.rs:33:32
    |
 LL |         let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -21,7 +21,7 @@ LL |         let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); //
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
 error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:33:36
+  --> $DIR/transmute_undefined_repr.rs:34:36
    |
 LL |         let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -29,7 +29,7 @@ LL |         let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); //
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
 error: transmute from `Ty<&Ty2<u32, i32>>` to `&Ty2<u32, f32>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:38:33
+  --> $DIR/transmute_undefined_repr.rs:39:33
    |
 LL |         let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -37,7 +37,7 @@ LL |         let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); /
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
 error: transmute from `&Ty2<u32, f32>` to `Ty<&Ty2<u32, i32>>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:39:37
+  --> $DIR/transmute_undefined_repr.rs:40:37
    |
 LL |         let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -45,7 +45,7 @@ LL |         let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); /
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
 error: transmute from `std::boxed::Box<Ty2<u32, u32>>` to `&mut Ty2<u32, f32>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:56:45
+  --> $DIR/transmute_undefined_repr.rs:57:45
    |
 LL |         let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
    |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -53,12 +53,28 @@ LL |         let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32,
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
 error: transmute from `&mut Ty2<u32, f32>` to `std::boxed::Box<Ty2<u32, u32>>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:57:37
+  --> $DIR/transmute_undefined_repr.rs:58:37
    |
 LL |         let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
-error: aborting due to 8 previous errors
+error: transmute from `std::vec::Vec<Ty2<U, i32>>` to `std::vec::Vec<Ty2<T, u32>>`, both of which have an undefined layout
+  --> $DIR/transmute_undefined_repr.rs:138:35
+   |
+LL |         let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: two instances of the same generic type (`Vec`) may have different layouts
+
+error: transmute from `std::vec::Vec<Ty2<T, u32>>` to `std::vec::Vec<Ty2<U, i32>>`, both of which have an undefined layout
+  --> $DIR/transmute_undefined_repr.rs:139:35
+   |
+LL |         let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: two instances of the same generic type (`Vec`) may have different layouts
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
index d9b64a0ed7b..de9418c8d1a 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
@@ -34,13 +34,13 @@ error: transmute from a reference to a pointer
 LL |     let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]`
 
-error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead
+error: transmute from `fn(usize) -> u8` to `*const usize` which could be expressed as a pointer cast instead
   --> $DIR/transmutes_expressible_as_ptr_casts.rs:48:41
    |
 LL |     let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize`
 
-error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead
+error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a pointer cast instead
   --> $DIR/transmutes_expressible_as_ptr_casts.rs:52:49
    |
 LL |     let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
diff --git a/src/tools/clippy/tests/ui/unnecessary_join.fixed b/src/tools/clippy/tests/ui/unnecessary_join.fixed
new file mode 100644
index 00000000000..7e12c6ae4be
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_join.fixed
@@ -0,0 +1,35 @@
+// run-rustfix
+
+#![warn(clippy::unnecessary_join)]
+
+fn main() {
+    // should be linted
+    let vector = vec!["hello", "world"];
+    let output = vector
+        .iter()
+        .map(|item| item.to_uppercase())
+        .collect::<String>();
+    println!("{}", output);
+
+    // should be linted
+    let vector = vec!["hello", "world"];
+    let output = vector
+        .iter()
+        .map(|item| item.to_uppercase())
+        .collect::<String>();
+    println!("{}", output);
+
+    // should not be linted
+    let vector = vec!["hello", "world"];
+    let output = vector
+        .iter()
+        .map(|item| item.to_uppercase())
+        .collect::<Vec<String>>()
+        .join("\n");
+    println!("{}", output);
+
+    // should not be linted
+    let vector = vec!["hello", "world"];
+    let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
+    println!("{}", output);
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_join.rs b/src/tools/clippy/tests/ui/unnecessary_join.rs
new file mode 100644
index 00000000000..0a21656a755
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_join.rs
@@ -0,0 +1,37 @@
+// run-rustfix
+
+#![warn(clippy::unnecessary_join)]
+
+fn main() {
+    // should be linted
+    let vector = vec!["hello", "world"];
+    let output = vector
+        .iter()
+        .map(|item| item.to_uppercase())
+        .collect::<Vec<String>>()
+        .join("");
+    println!("{}", output);
+
+    // should be linted
+    let vector = vec!["hello", "world"];
+    let output = vector
+        .iter()
+        .map(|item| item.to_uppercase())
+        .collect::<Vec<_>>()
+        .join("");
+    println!("{}", output);
+
+    // should not be linted
+    let vector = vec!["hello", "world"];
+    let output = vector
+        .iter()
+        .map(|item| item.to_uppercase())
+        .collect::<Vec<String>>()
+        .join("\n");
+    println!("{}", output);
+
+    // should not be linted
+    let vector = vec!["hello", "world"];
+    let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
+    println!("{}", output);
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_join.stderr b/src/tools/clippy/tests/ui/unnecessary_join.stderr
new file mode 100644
index 00000000000..0b14b143aff
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_join.stderr
@@ -0,0 +1,20 @@
+error: called `.collect<Vec<String>>().join("")` on an iterator
+  --> $DIR/unnecessary_join.rs:11:10
+   |
+LL |           .collect::<Vec<String>>()
+   |  __________^
+LL | |         .join("");
+   | |_________________^ help: try using: `collect::<String>()`
+   |
+   = note: `-D clippy::unnecessary-join` implied by `-D warnings`
+
+error: called `.collect<Vec<String>>().join("")` on an iterator
+  --> $DIR/unnecessary_join.rs:20:10
+   |
+LL |           .collect::<Vec<_>>()
+   |  __________^
+LL | |         .join("");
+   | |_________________^ help: try using: `collect::<String>()`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
index 4ba2a0a5dbc..65fcdc43061 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
@@ -115,6 +115,14 @@ fn main() {
     let _: Result<usize, usize> = res.or(Ok(2));
     let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
     let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
+    let _: Result<usize, usize> = res.
+        // some lines
+        // some lines
+        // some lines
+        // some lines
+        // some lines
+        // some lines
+        or(Ok(ext_str.some_field));
 
     // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
     let _: Result<usize, usize> = res.and_then(|x| Err(x));
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
index 466915217e4..206080ed69a 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
@@ -115,6 +115,14 @@ fn main() {
     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
+    let _: Result<usize, usize> = res.
+        // some lines
+        // some lines
+        // some lines
+        // some lines
+        // some lines
+        // some lines
+        or_else(|_| Ok(ext_str.some_field));
 
     // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
     let _: Result<usize, usize> = res.and_then(|x| Err(x));
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
index cc94bd5cd9e..7e4dd7730e7 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
@@ -2,7 +2,9 @@ error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:35:13
    |
 LL |     let _ = opt.unwrap_or_else(|| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(2)`
+   |             ^^^^--------------------
+   |                 |
+   |                 help: use `unwrap_or(..)` instead: `unwrap_or(2)`
    |
    = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
 
@@ -10,187 +12,264 @@ error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:36:13
    |
 LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)`
+   |             ^^^^---------------------------------
+   |                 |
+   |                 help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:37:13
    |
 LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)`
+   |             ^^^^-------------------------------------
+   |                 |
+   |                 help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:39:13
    |
 LL |     let _ = opt.and_then(|_| ext_opt);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `opt.and(ext_opt)`
+   |             ^^^^---------------------
+   |                 |
+   |                 help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:40:13
    |
 LL |     let _ = opt.or_else(|| ext_opt);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(ext_opt)`
+   |             ^^^^-------------------
+   |                 |
+   |                 help: use `or(..)` instead: `or(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:41:13
    |
 LL |     let _ = opt.or_else(|| None);
-   |             ^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(None)`
+   |             ^^^^----------------
+   |                 |
+   |                 help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:42:13
    |
 LL |     let _ = opt.get_or_insert_with(|| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `opt.get_or_insert(2)`
+   |             ^^^^------------------------
+   |                 |
+   |                 help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:43:13
    |
 LL |     let _ = opt.ok_or_else(|| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `opt.ok_or(2)`
+   |             ^^^^----------------
+   |                 |
+   |                 help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:44:13
    |
 LL |     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))`
+   |             ^^^^^^^^^^^^^^^^^-------------------------------
+   |                              |
+   |                              help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:47:13
    |
 LL |     let _ = Some(10).unwrap_or_else(|| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Some(10).unwrap_or(2)`
+   |             ^^^^^^^^^--------------------
+   |                      |
+   |                      help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:48:13
    |
 LL |     let _ = Some(10).and_then(|_| ext_opt);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `Some(10).and(ext_opt)`
+   |             ^^^^^^^^^---------------------
+   |                      |
+   |                      help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:49:28
    |
 LL |     let _: Option<usize> = None.or_else(|| ext_opt);
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(ext_opt)`
+   |                            ^^^^^-------------------
+   |                                 |
+   |                                 help: use `or(..)` instead: `or(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:50:13
    |
 LL |     let _ = None.get_or_insert_with(|| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `None.get_or_insert(2)`
+   |             ^^^^^------------------------
+   |                  |
+   |                  help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:51:35
    |
 LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
-   |                                   ^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `None.ok_or(2)`
+   |                                   ^^^^^----------------
+   |                                        |
+   |                                        help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:52:28
    |
 LL |     let _: Option<usize> = None.or_else(|| None);
-   |                            ^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(None)`
+   |                            ^^^^^----------------
+   |                                 |
+   |                                 help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:55:13
    |
 LL |     let _ = deep.0.unwrap_or_else(|| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `deep.0.unwrap_or(2)`
+   |             ^^^^^^^--------------------
+   |                    |
+   |                    help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:56:13
    |
 LL |     let _ = deep.0.and_then(|_| ext_opt);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `deep.0.and(ext_opt)`
+   |             ^^^^^^^---------------------
+   |                    |
+   |                    help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:57:13
    |
 LL |     let _ = deep.0.or_else(|| None);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(None)`
+   |             ^^^^^^^----------------
+   |                    |
+   |                    help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:58:13
    |
 LL |     let _ = deep.0.get_or_insert_with(|| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `deep.0.get_or_insert(2)`
+   |             ^^^^^^^------------------------
+   |                    |
+   |                    help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:59:13
    |
 LL |     let _ = deep.0.ok_or_else(|| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `deep.0.ok_or(2)`
+   |             ^^^^^^^----------------
+   |                    |
+   |                    help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:79:28
    |
 LL |     let _: Option<usize> = None.or_else(|| Some(3));
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(Some(3))`
+   |                            ^^^^^-------------------
+   |                                 |
+   |                                 help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:80:13
    |
 LL |     let _ = deep.0.or_else(|| Some(3));
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(Some(3))`
+   |             ^^^^^^^-------------------
+   |                    |
+   |                    help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> $DIR/unnecessary_lazy_eval.rs:81:13
    |
 LL |     let _ = opt.or_else(|| Some(3));
-   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(Some(3))`
+   |             ^^^^-------------------
+   |                 |
+   |                 help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval.rs:87:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(2)`
+   |             ^^^^^---------------------
+   |                  |
+   |                  help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval.rs:88:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)`
+   |             ^^^^^----------------------------------
+   |                  |
+   |                  help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval.rs:89:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)`
+   |             ^^^^^--------------------------------------
+   |                  |
+   |                  help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval.rs:111:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(2))`
+   |                                   ^^^^--------------------
+   |                                       |
+   |                                       help: use `and(..)` instead: `and(Err(2))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval.rs:112:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(astronomers_pi))`
+   |                                   ^^^^---------------------------------
+   |                                       |
+   |                                       help: use `and(..)` instead: `and(Err(astronomers_pi))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval.rs:113:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(ext_str.some_field))`
+   |                                   ^^^^-------------------------------------
+   |                                       |
+   |                                       help: use `and(..)` instead: `and(Err(ext_str.some_field))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval.rs:115:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(2))`
+   |                                   ^^^^------------------
+   |                                       |
+   |                                       help: use `or(..)` instead: `or(Ok(2))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval.rs:116:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(astronomers_pi))`
+   |                                   ^^^^-------------------------------
+   |                                       |
+   |                                       help: use `or(..)` instead: `or(Ok(astronomers_pi))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval.rs:117:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(ext_str.some_field))`
+   |                                   ^^^^-----------------------------------
+   |                                       |
+   |                                       help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
 
-error: aborting due to 32 previous errors
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:118:35
+   |
+LL |       let _: Result<usize, usize> = res.
+   |  ___________________________________^
+LL | |         // some lines
+LL | |         // some lines
+LL | |         // some lines
+...  |
+LL | |         // some lines
+LL | |         or_else(|_| Ok(ext_str.some_field));
+   | |_________----------------------------------^
+   |           |
+   |           help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
+
+error: aborting due to 33 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
index 75674b0a9d2..20acab6e844 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
@@ -2,7 +2,9 @@ error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval_unfixable.rs:12:13
    |
 LL |     let _ = Ok(1).unwrap_or_else(|()| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)`
+   |             ^^^^^^----------------------
+   |                   |
+   |                   help: use `unwrap_or(..)` instead: `unwrap_or(2)`
    |
    = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
 
@@ -10,13 +12,17 @@ error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval_unfixable.rs:16:13
    |
 LL |     let _ = Ok(1).unwrap_or_else(|e::E| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)`
+   |             ^^^^^^------------------------
+   |                   |
+   |                   help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13
    |
 LL |     let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)`
+   |             ^^^^^^-------------------------------------
+   |                   |
+   |                   help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
index 720138db137..38ba41ac54e 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
@@ -212,3 +212,51 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E
 }
 
 fn require_string(_: &String) {}
+
+// https://github.com/rust-lang/rust-clippy/issues/8507
+mod issue_8507 {
+    #![allow(dead_code)]
+
+    struct Opaque<P>(P);
+
+    pub trait Abstracted {}
+
+    impl<P> Abstracted for Opaque<P> {}
+
+    fn build<P>(p: P) -> Opaque<P>
+    where
+        P: AsRef<str>,
+    {
+        Opaque(p)
+    }
+
+    // Should not lint.
+    fn test_str(s: &str) -> Box<dyn Abstracted> {
+        Box::new(build(s.to_string()))
+    }
+
+    // Should not lint.
+    fn test_x(x: super::X) -> Box<dyn Abstracted> {
+        Box::new(build(x))
+    }
+
+    #[derive(Clone, Copy)]
+    struct Y(&'static str);
+
+    impl AsRef<str> for Y {
+        fn as_ref(&self) -> &str {
+            self.0
+        }
+    }
+
+    impl ToString for Y {
+        fn to_string(&self) -> String {
+            self.0.to_string()
+        }
+    }
+
+    // Should lint because Y is copy.
+    fn test_y(y: Y) -> Box<dyn Abstracted> {
+        Box::new(build(y))
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
index 60b2e718f5d..15fb7ee83e3 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
@@ -212,3 +212,51 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E
 }
 
 fn require_string(_: &String) {}
+
+// https://github.com/rust-lang/rust-clippy/issues/8507
+mod issue_8507 {
+    #![allow(dead_code)]
+
+    struct Opaque<P>(P);
+
+    pub trait Abstracted {}
+
+    impl<P> Abstracted for Opaque<P> {}
+
+    fn build<P>(p: P) -> Opaque<P>
+    where
+        P: AsRef<str>,
+    {
+        Opaque(p)
+    }
+
+    // Should not lint.
+    fn test_str(s: &str) -> Box<dyn Abstracted> {
+        Box::new(build(s.to_string()))
+    }
+
+    // Should not lint.
+    fn test_x(x: super::X) -> Box<dyn Abstracted> {
+        Box::new(build(x))
+    }
+
+    #[derive(Clone, Copy)]
+    struct Y(&'static str);
+
+    impl AsRef<str> for Y {
+        fn as_ref(&self) -> &str {
+            self.0
+        }
+    }
+
+    impl ToString for Y {
+        fn to_string(&self) -> String {
+            self.0.to_string()
+        }
+    }
+
+    // Should lint because Y is copy.
+    fn test_y(y: Y) -> Box<dyn Abstracted> {
+        Box::new(build(y.to_string()))
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
index 1dfc65e22e2..c53ce32be77 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
@@ -491,5 +491,11 @@ LL -         let path = match get_file_path(&t) {
 LL +         let path = match get_file_path(t) {
    | 
 
-error: aborting due to 76 previous errors
+error: unnecessary use of `to_string`
+  --> $DIR/unnecessary_to_owned.rs:260:24
+   |
+LL |         Box::new(build(y.to_string()))
+   |                        ^^^^^^^^^^^^^ help: use: `y`
+
+error: aborting due to 77 previous errors